././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1581856610.6392903 fava-1.14/0000755000175000001440000000000000000000000012622 5ustar00jakobusers00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1581856610.5992906 fava-1.14/.builds/0000755000175000001440000000000000000000000014162 5ustar00jakobusers00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1579965050.0 fava-1.14/.builds/archlinux.yml0000644000175000001440000000030200000000000016675 0ustar00jakobusers00000000000000image: archlinux packages: - python - nodejs - npm - python-tox sources: - https://github.com/beancount/fava tasks: - build: | make -C fava - test: | make -C fava test ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1579964989.0 fava-1.14/.builds/debian.yml0000644000175000001440000000036700000000000016135 0ustar00jakobusers00000000000000image: debian/stable packages: - python3-dev - nodejs - npm - tox sources: - https://github.com/beancount/fava tasks: - build: | sudo npm install npm@latest -g --no-progress make -C fava - test: | make -C fava test ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/AUTHORS0000644000175000001440000000073400000000000013676 0ustar00jakobusers00000000000000Fava was created by Dominik Aumayr . Fava is maintained by Dominik Aumayr and Jakob Schnitzer. See the Issue Tracker and the Git log for other contributors, e.g., with: git log --format='%aN' | sort -u Translations: The translations are maintained on POEditor. See the project there (https://poeditor.com/join/project/TlraSxOCxt) for details. A big thank you goes to Martin Blais for creating Beancount and all the invaluable documentation for it. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/CHANGES0000644000175000001440000003136100000000000013621 0ustar00jakobusers00000000000000Changelog ========= Unreleased ---------- Nothing yet. v1.13 (2020-02-01) ------------------ Fava can now display charts for BQL queries - if they have exactly two columns with the first being a date or string and the second an inventory, then a line chart or treemap chart is shown on the query page. v1.12 (2019-12-03) ------------------ Apart from plenty of bug fixes, this release mainly contains improvements to the forms to add transactions: postings can now be dragged and the full cost syntax of Beancount should supported. v1.11 (2019-08-20) ------------------ The import page of Fava has been reworked - it now supports moving files to the documents folder and the import process should be a bit more interactive. This release also contains various fixes and a new `collapse-pattern` option to collapse accounts in account trees based on regular expressions (and replaces the use of the `fava-collapse-account` metadata entry). Other changes: - Command line flags can be specified by setting environment variables. - A Taiwanese translation has been added. v1.10 (2019-01-31) ------------------ This release contains mostly smaller changes and fixes. In particular, the net worth chart will now follow the selected conversion. v1.9 (2018-10-08) ----------------- In this release, the click behaviour has been updated to allow filtering for payees. The entry input forms now allow inputting prices and costs. As always, bugs have been fixed. v1.8 (2018-07-25) ----------------- The journal design has been updated and should now have a clearer structure. Starting with this version, there will not be any more GUI releases of Fava. The GUI broke frequently and does not seem to worth the maintenance burden. Other changes: - When downloading documents, the original filename will be used. - `any()` and `all()` functions have been added to the filter syntax to allow filtering entries by properties of their postings. - As always, bugs have been fixed. v1.7 (2018-03-09) ----------------- The entry filters have been reworked in this release and should now support for more flexible filtering of the entries. See the help page on how the new syntax works. Also, when completing the payee in the transaction form, the postings of the last transaction for this payee will be auto-filled. Other changes: - The fava-option to hide the charts has been removed. This is now tracked in the page URL. - As always, bugs have been fixed. v1.6 (2017-10-06) ----------------- This is a release with various small changes and mainly some speed improvements to the Balance Sheet and the net worth calculation. Also, if 'At Value' is selected, the current unrealized gain is shown in parentheses in the Balance Sheet. Other changes: - The currently filtered entries can now be exported from the Journal page. - The CLI now has a ``--version`` flag. v1.5 (2017-07-23) ----------------- Fava now has an interface to edit single entries. Clicking on the entry date in the Journal will open an overlay that shows the entry context and allows editing just the lines of that entry. Other changes: - The source editor now has a menu that gives access to editor commands like "fold all". - Entries with matching tags or links can now be excluded with ``-#tag``. - The keyboard shortcuts are now displayed in-place. - The ``incognito`` option has been removed and replaced with a ``--incognito`` command line switch. - As always, several bugs have been fixed. v1.4 (2017-05-14) ----------------- Fava now provides an interface for Beancount's import system that allows you to import transactions from your bank for example. Fava can now show your balances at market value or convert them to a single currency if your file contains the necessary price information. We now also provide a compiled GUI version of Fava for Linux and macOS. This version might still be a bit buggy so any feedback/help on it is very welcome. Other changes: - The ``insert-entry`` option can be used to control where transactions are inserted. - The transaction form now accepts tags and links in the narration field. - Budgets are now accumulated over all children where appropriate. - As always, several bugs have been fixed. Thanks to :user:`TZdyrski` and :user:`Akuukis` for their contributions. v1.3 (2017-03-15) ----------------- The translations of Fava are now on `POEditor.com `__, which has helped us get translations in five more languages: Chinese (simplified), Dutch, French, Portuguese, and Spanish. A big thank you to the new translators! The transaction form has been improved, it now supports adding metadata and the suggestions will be ranked by how often and recently they occur (using exponential decay). The Query page supports all commands of the ``bean-query`` shell and shares its history of recently used queries. Fava has gained a basic extension mechanism. Extensions allow you to run hooks at various points, e.g., after adding a transaction. They are specified using the ``extensions`` option and for an example, see the ``fava.ext.auto_commit`` extension. Other changes: - The default sort order in journals has been reversed so that the most recent entries come first. - The new ``incognito`` option can be used to obscure all numbers. - As always, several bugs have been fixed. Thanks to :user:`johannesharms` and :user:`xentac` for their contributions. v1.2 (2016-12-25) ----------------- You can now add transactions from within Fava. The form supports autocompletion for most fields. Fava will now show a little bubble in the sidebar for the number of events in the next week. This can be configured with the ``upcoming-events`` option. Other changes: - The payee filter can filter by regular expression. - The tag filter can filter for links, too. - There's a nice spinning indicator during asynchronous page loads. - The Journal shows little indicators for metadata. - As always, several bugs have been fixed. Thanks to :user:`fokusov` for their contributions. v1.1 (2016-11-19) ----------------- You can now upload documents by dropping them onto transactions, which will also add the file path as `statement` metadata to the transaction. Fava also ships with a plugin to link these transactions with the generated documents. See the help pages for details. This is the first release for which we provide compiled binaries (for macOS and Linux). These do not have any dependencies and can simply be executed from the terminal. Other changes: - The bar charts on account pages now also show budgets. - The Journal can now be sorted by date, flag and narration. - Fava now has a Russian translation, thanks to :user:`fokusov`. - As always, several bugs have been fixed. Thanks to :user:`adamgibbins` and :user:`xentac` for their contributions. v1.0 (2016-10-19) ----------------- This is a major new release that includes too many improvements and changes to list. Some highlights: - The layout has been tweaked and we use some nicer fonts. - Fava looks and works much better on smaller screens. - Fava loads most pages asynchronously, so navigating Fava is much faster and responsive. Fava's configuration is not read from a configuration file anymore but can rather be specified using custom entries in the Beancount file. Some options have also been removed or renamed, so check Fava's help page on the available options when upgrading from v0.3.0. There have been many changes under the hood to improve Fava's codebase and a lot of bugs have been squashed. Thanks to :user:`adamgibbins`, :user:`davidastephens`, :user:`xentac`, and :user:`yegle` for their contributions. v0.3.0 (2016-03-24) ------------------- Additions - Support for switching between multiple beancount files. :bug:`213` - New sunburst charts. :bug:`198` - Add "Clear filter" button when filters are active. :bug:`290` - Simple budgeting functionality in the Account view. See help pages on how to use budgets. :bug:`294` - German translation. :bug:`284` - The Beancount is now being reloaded when it is saved in the Source Editor. - New Journal filter controls. Thanks to :user:`yagebu`. - Tree-tables are now displayed in a hierarchical way. Thanks to :user:`yagebu`. Changes - All charts are now rendered with d3.js. Thanks to :user:`yagebu`. - The title of a page is now shown in the header to save screen space. - Changed shortcut for Journal from ``g g`` to ``g j`` as the Journal was renamed from "General Journal" to "Journal". New configuration options - ``language``: The language to use. Valid languages are ``"en"`` and ``"de"`` (default: ``"en"``). :bug:`284` - ``treemaps-show-negative-numbers`` was removed. Fixes - Commodity prices are now filtered when a Time filter is enabled. :bug:`273` - Some improvements to the help pages. - Many small bug fixes. Thanks to :user:`yagebu`. v0.2.6 (2016-03-20) ------------------- Additions - There are now more interval options available for charts and the account balances report. The interval can be selected from a dropdown next to the charts. :bug:`175` - Show metadata for postings in the Journal. Thanks to :user:`corani`. :bug:`185` - The editor now supports org-mode style folding. Thanks to :user:`corani`. :bug:`209` - Show colored dots for all the postings of a transaction in the Journal report. This way flagged postings can be quickly spotted. :bug:`195` - Add keyboard shortcuts for save to source editor. :bug:`199` Changes - Use beancount's DisplayContext to determine the correct precision at which to render numbers. :bug:`188` - Improve the way that query results are serialized to XLS etc. Thanks to :user:`corani`. :bug:`168` - Show inverse rates for pairs of operating currencies on the commodities report. :bug:`139` - Use Click for the CLI and check if beancount file exists on startup. :bug:`216` - Hide closed accounts in tree tables. Also see new configuration option below. New configuration options - ``editor-strip-trailing-whitespace`` to enable trimming of trailing whitespace in the Source editor (default: "false"). Thanks to :user:`corani`. :bug:`163` - ``show-closed-accounts`` to show closed accounts in tree tables, for example on the balance sheet (default: "false"). :bug:`91` - ``show-accounts-with-zero-balance`` to show accounts with a balance of zero in tree tables (default: "true"). :bug:`91` - ``show-accounts-with-zero-transactions`` to show accounts with no transactions in tree tables (default: "true"). :bug:`91` Fixes - Fixed a bug where the months would be off by one for the interval reports. :bug:`182` - Fix the net worth report for more than one currency. :bug:`207` - Some improvements to the help pages. - Many small bug fixes. v0.2.5 (2016-02-28) ------------------- Bump release to remove unused draft code. v0.2.4 (2016-02-18) ------------------- Additions - Added missing Holdings views compared to ``bean-web``. Thanks to :user:`yagebu`. :bug:`140` - Custom queries are now shown in sidebar. Thanks to :user:`corani`. :bug:`135` - The user settings file is now editable in the Source editor. :bug:`136` - Added second theme. Thanks to Rubén Gómez for the stylesheet. :bug:`59` - Added Help pages. - Query results can now be downloaded as CSV, XLS, XLSX and ODS. :bug:`143` - Documents can now be uploaded by dragging and dropping files over an Account name on the Account page and all tree-tables. :bug:`157` - Journal can now be filtered by transaction type. Thanks to :user:`yagebu`. Changes - The uptodate-indicator is now shown everywhere by default, but only enabled for accounts that have the metadata ``fava-uptodate-indication: "True"`` set on their ``open``-directives. :bug:`35` - Speedier Journal rendering. Thanks to :user:`yagebu`. :bug:`164` - Only basenames will be shown for documents in the Journal. Thanks to :user:`corani`. - Slightly reordered the sidebar menu. - Minor UI tweaks. New configuration options - ``sidebar-show-queries``: The maximum number of custom queries to show in the sidebar (default: 5). - ``theme``: The theme to use. Valid themes are ``"default"`` and ``"alternative"`` (default: ``"default"``). - ``editor-print-margin-column``: Set the column for the print margin in the Source editor (default: 60). :bug:`161` - ``uptodate-indicator-show-everywhere`` (default: "true"). See Changes above. Removed configuration options - ``uptodate-indicator-exclude-accounts``, see Changes above. Fixes - Fixed Net worth calculation. Thanks to :user:`yagebu`. - Many small bug fixes. v0.2.3 (2016-02-15) ------------------- Bumped version to communicate that installing via ``pip install`` now works, all requirements included. Thanks to :user:`blais` and :user:`yagebu`. Earlier Versions ---------------- It was not possible to install any of the earlier versions only using ``pip`` and you may consult the git log for earlier changes. The first commit in the git repository was on December 4th, 2015. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/CONTRIBUTING.md0000644000175000001440000000153400000000000015056 0ustar00jakobusers00000000000000### Reporting bugs and suggestions Please first search the existing issues, including closed ones, for a report that matches your issue. When reporting a bug, provide details of the Fava version and browser used. ### Contributing code See [here](https://beancount.github.io/fava/development.html) how to setup a development environment. Make a fork and submit your Pull Request. If it's a large change or you want some help to get started with coding, open an issue beforehand to discuss it or just ask about it in the [chat](https://gitter.im/beancount/fava). Please contribute tests as well. See the `tests/` subdirectory for existing tests, which can be run with `make test` (requires `tox`). ### Contributing translations You can contribute to Fava's translations at the [Fava project on POEditor.com](https://poeditor.com/join/project/TlraSxOCxt). ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/LICENSE0000644000175000001440000000212400000000000013626 0ustar00jakobusers00000000000000The MIT License (MIT) Copyright (c) 2015-2016 Dominik Aumayr Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/MANIFEST.in0000644000175000001440000000046300000000000014363 0ustar00jakobusers00000000000000# setuptools_scm ensure all required files are included, # we only need excludes here exclude .editorconfig .gitignore .travis.yml appveyor.yml prune fava/static graft fava/static/gen graft fava/static/images exclude fava/translations/babel.conf global-exclude *.po *.py[co] *.orig __pycache__ .DS_Store ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1581530070.0 fava-1.14/Makefile0000644000175000001440000000512400000000000014264 0ustar00jakobusers00000000000000all: fava/static/gen/app.js fava/static/gen/app.js: fava/static/css/* fava/static/javascript/* fava/static/package.json fava/static/node_modules cd fava/static; npm run build fava/static/node_modules: fava/static/package-lock.json cd fava/static; npm install --no-progress touch -m fava/static/node_modules .PHONY: clean clean: mostlyclean rm -rf build dist rm -rf fava/static/gen .PHONY: mostlyclean mostlyclean: rm -rf .*cache rm -rf .eggs rm -rf .tox rm -rf build rm -rf dist rm -rf fava/static/node_modules find . -type f -name '*.py[c0]' -delete find . -type d -name "__pycache__" -delete .PHONY: check-lint check-lint: fava/static/node_modules cd fava/static; npm run check-lint tox -e lint .PHONY: lint lint: fava/static/node_modules cd fava/static; npm run lint tox -e format tox -e lint .PHONY: test test: tox .PHONY: update-snapshots update-snapshots: find . -name "__snapshots__" -type d -prune -exec rm -r "{}" + -SNAPSHOT_UPDATE=1 tox tox .PHONY: docs docs: tox -e docs .PHONY: run-example run-example: @xdg-open http://localhost:3333 BEANCOUNT_FILE= fava -p 3333 --debug tests/data/example.beancount .PHONY: bql-grammar bql-grammar: contrib/scripts.py generate-bql-grammar-json dist: fava/static/gen/app.js fava setup.cfg setup.py MANIFEST.in rm -rf build dist python setup.py sdist bdist_wheel .PHONY: before-release before-release: bql-grammar translations-push translations-fetch # Before making a release, CHANGES needs to be updated and # a tag should be created too. # # Also, fava.pythonanywhere.com should be updated. .PHONY: upload upload: dist twine upload dist/* # Extract translation strings. .PHONY: translations-extract translations-extract: pybabel extract -F fava/translations/babel.conf -o fava/translations/messages.pot ./fava # Extract translation strings and upload them to POEditor.com. # Requires the environment variable POEDITOR_TOKEN to be set to an API token # for POEditor. .PHONY: translations-push translations-push: translations-extract contrib/scripts.py upload-translations # Download translations from POEditor.com. (also requires POEDITOR_TOKEN) .PHONY: translations-fetch translations-fetch: contrib/scripts.py download-translations pybabel compile -d fava/translations # Build and upload the website. .PHONY: gh-pages gh-pages: git checkout master git checkout --orphan gh-pages tox -e docs ls | grep -v 'build' | xargs rm -r mv -f build/docs/* ./ rm -r build touch .nojekyll git add -A git commit -m 'Update gh-pages' git push --force git@github.com:beancount/fava.git gh-pages:gh-pages git checkout master git branch -D gh-pages ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1581856610.6392903 fava-1.14/PKG-INFO0000644000175000001440000000571500000000000013727 0ustar00jakobusers00000000000000Metadata-Version: 2.1 Name: fava Version: 1.14 Summary: Web interface for the accounting tool Beancount. Home-page: https://beancount.github.io/fava/ Author: Dominik Aumayr Author-email: dominik@aumayr.name Maintainer: Jakob Schnitzer Maintainer-email: mail@jakobschnitzer.de License: MIT Description: .. image:: https://badges.gitter.im/beancount/fava.svg :alt: Join the chat at https://gitter.im/beancount/fava :target: https://gitter.im/beancount/fava .. image:: https://img.shields.io/pypi/l/fava.svg :target: https://pypi.python.org/pypi/fava .. image:: https://img.shields.io/pypi/v/fava.svg :target: https://pypi.python.org/pypi/fava .. image:: https://img.shields.io/travis/beancount/fava.svg :target: https://travis-ci.org/beancount/fava?branch=master Fava is a web interface for the double-entry bookkeeping software `Beancount `__ with a focus on features and usability. Check out the online `demo `__ and learn more about Fava on the `website `__. The `Getting Started `__ guide details the installation and how to get started with Beancount. If you are already familiar with Beancount, you can get started with Fava:: pip3 install fava fava ledger.beancount and visit the web interface at `http://localhost:5000 `__. If you want to hack on Fava or run a development version, see the `Development `__ page on the website for details. Contributions are very welcome! .. image:: https://i.imgbox.com/rfb9I7Zw.png :alt: Fava Screenshot :width: 100% :align: center :target: https://fava.pythonanywhere.com Keywords: fava beancount accounting Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Framework :: Flask Classifier: Intended Audience :: Education Classifier: Intended Audience :: End Users/Desktop Classifier: Intended Audience :: Financial and Insurance Industry Classifier: Intended Audience :: Information Technology Classifier: License :: OSI Approved :: MIT License Classifier: Natural Language :: English Classifier: Programming Language :: JavaScript Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Office/Business :: Financial :: Accounting Classifier: Topic :: Office/Business :: Financial :: Investment Provides-Extra: excel ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/README.rst0000644000175000001440000000270300000000000014313 0ustar00jakobusers00000000000000.. image:: https://badges.gitter.im/beancount/fava.svg :alt: Join the chat at https://gitter.im/beancount/fava :target: https://gitter.im/beancount/fava .. image:: https://img.shields.io/pypi/l/fava.svg :target: https://pypi.python.org/pypi/fava .. image:: https://img.shields.io/pypi/v/fava.svg :target: https://pypi.python.org/pypi/fava .. image:: https://img.shields.io/travis/beancount/fava.svg :target: https://travis-ci.org/beancount/fava?branch=master Fava is a web interface for the double-entry bookkeeping software `Beancount `__ with a focus on features and usability. Check out the online `demo `__ and learn more about Fava on the `website `__. The `Getting Started `__ guide details the installation and how to get started with Beancount. If you are already familiar with Beancount, you can get started with Fava:: pip3 install fava fava ledger.beancount and visit the web interface at `http://localhost:5000 `__. If you want to hack on Fava or run a development version, see the `Development `__ page on the website for details. Contributions are very welcome! .. image:: https://i.imgbox.com/rfb9I7Zw.png :alt: Fava Screenshot :width: 100% :align: center :target: https://fava.pythonanywhere.com ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1581856610.5992906 fava-1.14/contrib/0000755000175000001440000000000000000000000014262 5ustar00jakobusers00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/contrib/deployment.rst0000644000175000001440000000167000000000000017200 0ustar00jakobusers00000000000000Deployment ========== There are a number of deployment options for persistently running Fava on the Web, depending on your Web server and WSGI deployment choices. Below you can find some examples. Apache with reverse proxy ------------------------- Apache configuration:: ProxyPass "/fava" "http://localhost:5000/fava" The above will make Fava accessible at the ``/fava`` URL and proxy requests arriving there to a locally running Fava. To make Fava work properly in that context, you should run it using the ``--prefix`` command line option, like this:: fava --prefix /fava /path/to/your/main.beancount To have Fava run automatically at boot and manageable as a system service you might want to define a systemd unit file for it, for example:: [Unit] Description=Fava Web UI for Beancount [Service] Type=simple ExecStart=/usr/bin/fava --host localhost --port 5000 --prefix /fava /path/to/your/main.beancount User=your-user ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1581856610.5992906 fava-1.14/contrib/docker/0000755000175000001440000000000000000000000015531 5ustar00jakobusers00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/contrib/docker/Dockerfile0000644000175000001440000000046000000000000017523 0ustar00jakobusers00000000000000FROM python:alpine as builder RUN apk add --update libxml2-dev libxslt-dev gcc musl-dev g++ RUN pip install --prefix="/install" fava FROM python:alpine COPY --from=builder /install /usr/local ENV BEANCOUNT_FILE "" ENV FAVA_OPTIONS "" EXPOSE 5000 CMD fava --host 0.0.0.0 $FAVA_OPTIONS $BEANCOUNT_FILE ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/contrib/docker/README.md0000644000175000001440000002276300000000000017022 0ustar00jakobusers00000000000000# Deploying with Docker ## Basic The Dockerfile in this directory is enough to get started running Fava in a container. This guide is meant as a compliment to the great documentation found at https://docs.docker.com/. ### Building To build the image using the provided Dockerfile run this command: ``` docker build -t fava . ``` This will build everything and name the image `fava`. Because docker depends heavily on caching to improve efficiency, to incorporate a new version of Beancount or Fava you must use the `--no-cache` flag when rebuilding the image. ### Deploying To run the Fava container, use this command: ``` docker run --detach --name="beancount" --publish 5000:5000 \ --volume $(pwd)/example.beancount:/input.beancount \ --env BEANCOUNT_FILE=/input.beancount fava ``` Let's look at each argument independently: 1. `--detach` tells Docker to start the image and run it in the background as a daemon. 1. `--name` specifies a name to give the Docker instance instead of generating a random id. This will be used later. 1. `--publish` tells Docker to expose the container's port 5000 as the local machine's port 5000. This allows us to access Fava with the url http://localhost:5000/. 1. `--volume` tells Docker to share the example.beancount file in the current directory to the container as the file `/input.beancount`. 1. `--env` tells Fava where to find the Beancount file. Going to http://localhost:5000/ will display your Fava instance. ## Advanced Hosting a local Docker instance is nice and all, but what we really want is a globally available, authenticated, secure deployment of Fava. To do that we need two other parts: oauth proxy and letsencrypt. ### Oauth proxy Oauth is an authentication standard that makes it easy to authenticate using a third party account. Using oauth means we can limit access to our site without requiring mobile users to enter complicated passwords. We will be using bitly's [oauth2_proxy](https://github.com/bitly/oauth2_proxy) to manage access to our site. I recommend using the [skippy/oauth2_proxy](https://hub.docker.com/r/skippy/oauth2_proxy/) Docker image. It is an alpinelinux-based Docker image with bitly's oauth2_proxy packaged. It uses an older version of oauth2_proxy, which is fine enough for Fava, but it is left as an exercise to the reader to build an updated version. It can be configured entirely using command line flags, but it is generally easier to configure using a file. Follow the Google Auth Provider instructions in the [oauth2_proxy](https://github.com/bitly/oauth2_proxy) README to generate a Client ID and Client Secret and fill out the `oauth2_proxy.cfg`. The file will look something like this: ``` ## OAuth2 Proxy Config File ## https://github.com/bitly/oauth2_proxy ## : to listen on for HTTP/HTTPS clients # http_address = "127.0.0.1:4180" # https_address = ":443" ## TLS Settings # tls_cert_file = "" # tls_key_file = "" ## the OAuth Redirect URL. # defaults to the "https://" + requested host header + "/oauth2/callback" redirect_url = "https://bean.xennet.org/oauth2/callback" ## the http url(s) of the upstream endpoint. If multiple, routing is based on path upstreams = [ "http://beancount:5000/" ] ## Log requests to stdout request_logging = true ## pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream # pass_basic_auth = true ## pass the request Host Header to upstream ## when disabled the upstream Host is used as the Host Header # pass_host_header = true ## Email Domains to allow authentication for (this authorizes any email on this domain) ## for more granular authorization use `authenticated_emails_file` ## To authorize any email addresses use "*" # email_domains = [ # "yourcompany.com" # ] ## The OAuth Client ID, Secret client_id = "" client_secret = "" ## Pass OAuth Access token to upstream via "X-Forwarded-Access-Token" # pass_access_token = false ## Authenticated Email Addresses File (one email per line) authenticated_emails_file = "/etc/authenticated-emails" ## Htpasswd File (optional) ## Additionally authenticate against a htpasswd file. Entries must be created with "htpasswd -s" for SHA encryption ## enabling exposes a username/login signin form # htpasswd_file = "" ## Templates ## optional directory with custom sign_in.html and error.html # custom_templates_dir = "" ## Cookie Settings ## Name - the cookie name ## Secret - the seed string for secure cookies; should be 16, 24, or 32 bytes ## for use with an AES cipher when cookie_refresh or pass_access_token ## is set ## Domain - (optional) cookie domain to force cookies to (ie: .yourcompany.com) ## Expire - (duration) expire timeframe for cookie ## Refresh - (duration) refresh the cookie when duration has elapsed after cookie was initially set. ## Should be less than cookie_expire; set to 0 to disable. ## On refresh, OAuth token is re-validated. ## (ie: 1h means tokens are refreshed on request 1hr+ after it was set) ## Secure - secure cookies are only sent by the browser of a HTTPS connection (recommended) ## HttpOnly - httponly cookies are not readable by javascript (recommended) # cookie_name = "_oauth2_proxy" cookie_secret = "" # cookie_domain = "" # cookie_expire = "168h" # cookie_refresh = "" cookie_secure = true cookie_httponly = true ``` Create an `authenticated-emails` file in the same directory filled with all the gmail accounts you want to have access to your Fava web interface. To run your proxy docker image use the following command: docker run --detach --link beancount --publish 4180:4180 \ --name beancount-oauth \ --volume $(pwd)/oauth2_proxy.cfg:/etc/oauth2_proxy.cfg \ --volume $(pwd)/authenticated-emails:/etc/authenticated-emails \ --env "VIRTUAL_HOST=" \ --env "LETSENCRYPT_HOST=" \ --env "LETSENCRYPT_EMAIL=" \ skippy/oauth2_proxy -config=/etc/oauth2_proxy.cfg \ -http-address="0.0.0.0:4180" -provider=google Let's document the new arguments: 1. `--link` tells docker to link one container to another, so they can access each other's exposed ports, and properly set up hostname mappings. This is why `upstreams` in the oauth2_proxy config is `http://beancount:5000`. 1. `--volume` maps host paths into docker instances. This is one way of getting data into a docker instance. 1. `--env` sets arbitrary environment values in docker instances. We will use these values in later sections to hook up automatically generate and refresh Let's Encrypt SSL certificates. Without a Let's Encrypt certificate, your oauth2_proxy cookie will be visible to anyone who can see your network traffic. You don't want this. 1. Everything after the `skippy/oauth2_proxy` are arguments to oauth2_proxy. This will start an oauth2 proxy using your config to do authentication using Google's OAuth service. It's important that you don't try to access your service immediately on port 4180, otherwise you may expose your authentication cookie to the internet. ### Letsencrypt [Let's Encrypt](https://letsencrypt.org/) is a very popular project aimed at making SSL certificates available to everyone for free. This will be a great way to secure our Fava instance so that we can share secret cookies without fear of anyone impersonating us. We don't just want to set up a static Let's Encrypt config. We want to use the magic of docker instances to generate our configs and keep them up to date at all times. To accomplish this we will use three separate docker instances working in conjunction. These instructions will mostly be a specific implementation of the instructions found in [JrCs/docker-letsencrypt-nginx-proxy-companion](https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion#separate-containers). We will run three separate containers: ``` docker run --detach --publish 80:80 --publish 443:443 \ --name nginx \ --volume /etc/nginx/conf.d \ --volume /etc/nginx/vhost.d \ --volume /usr/share/nginx/html \ --volume $(pwd)/certs:/etc/nginx/certs:ro \ --volume $(pwd)/htpasswd:/etc/nginx/htpasswd:ro \ --label com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy=true \ nginx ``` ``` docker run --detach \ --name nginx-gen \ --volumes-from nginx \ --volume /var/run/docker.sock:/tmp/docker.sock:ro \ jwilder/docker-gen \ -notify-sighup nginx -watch -only-exposed -wait 5s:30s \ /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf ``` ``` docker run --detach \ --name nginx-letsencrypt \ --env "NGINX_DOCKER_GEN_CONTAINER=nginx-gen" \ --volumes-from nginx \ --volume $(dirname $(realpath $0))/certs:/etc/nginx/certs:rw \ --volume /var/run/docker.sock:/var/run/docker.sock:ro \ jrcs/letsencrypt-nginx-proxy-companion ``` The main new argument in these three images is `--volumes-from`. This flag allows containers to share paths and make their data visible to each other. Another interesting thing is that the non-nginx docker images need access to the docker socket so that they can read the environment variables of other instances and also send sighups to the nginx process when configs change. Now all you have to do is expose port 80 and 443 from your host machine to the internet and point the domain you specified in `VIRTUAL_HOST` and `LETSENCRYPT_HOST` to your IP. Once all these parts are set up point your browser to your virtual host domain and enjoy a fully authenticated, secure, publicly addressable Fava instance. ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1581856610.602624 fava-1.14/contrib/examples/0000755000175000001440000000000000000000000016100 5ustar00jakobusers00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/contrib/examples/budgets-example.beancount0000644000175000001440000116535100000000000023102 0ustar00jakobusers00000000000000;; -*- mode: org; mode: beancount; -*- * Options option "title" "Example with budgets" option "operating_currency" "USD" * Commodities 1792-01-01 commodity USD export: "CASH" name: "US Dollar" 1900-01-01 commodity VMMXX export: "MUTF:VMMXX (MONEY:USD)" 1980-05-12 commodity VACHR export: "IGNORE" name: "Employer Vacation Hours" 1980-05-12 commodity IRAUSD export: "IGNORE" name: "US 401k and IRA Contributions" 1995-09-18 commodity VBMPX export: "MUTF:VBMPX" name: "Vanguard Total Bond Market Index Fund Institutional Plus Shares" price: "USD:google/MUTF:VBMPX" 2004-01-20 commodity ITOT export: "NYSEARCA:ITOT" name: "iShares Core S&P Total U.S. Stock Market ETF" price: "USD:google/NYSEARCA:ITOT" 2004-01-26 commodity VHT export: "NYSEARCA:VHT" name: "Vanguard Health Care ETF" price: "USD:google/NYSEARCA:VHT" 2004-11-01 commodity GLD export: "NYSEARCA:GLD" name: "SPDR Gold Trust (ETF)" price: "USD:google/NYSEARCA:GLD" 2007-07-20 commodity VEA export: "NYSEARCA:VEA" name: "Vanguard FTSE Developed Markets ETF" price: "USD:google/NYSEARCA:VEA" 2009-05-01 commodity RGAGX export: "MUTF:RGAGX" name: "American Funds The Growth Fund of America Class R-6" price: "USD:google/MUTF:RGAGX" * Equity Accounts 1980-05-12 open Equity:Opening-Balances 1980-05-12 open Liabilities:AccountsPayable * Banking 2015-01-01 open Assets:US:BofA address: "123 America Street, LargeTown, USA" institution: "Bank of America" phone: "+1.012.345.6789" 2015-01-01 open Assets:US:BofA:Checking USD account: "00234-48574897" 2015-01-01 * "Opening Balance for checking account" Assets:US:BofA:Checking 2820.05 USD Equity:Opening-Balances -2820.05 USD 2015-01-02 balance Assets:US:BofA:Checking 4170.65 USD 2015-01-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-01-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-01-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-01-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -46.66 USD Expenses:Home:Phone 46.66 USD 2015-01-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.13 USD Expenses:Home:Internet 80.13 USD 2015-01-31 balance Assets:US:BofA:Checking 4120.05 USD 2015-02-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-02-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-02-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-02-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -58.17 USD Expenses:Home:Phone 58.17 USD 2015-02-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.96 USD Expenses:Home:Internet 79.96 USD 2015-02-27 balance Assets:US:BofA:Checking 3519.70 USD 2015-03-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-03-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-03-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-03-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -56.78 USD Expenses:Home:Phone 56.78 USD 2015-03-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.97 USD Expenses:Home:Internet 79.97 USD 2015-03-27 balance Assets:US:BofA:Checking 3039.48 USD 2015-04-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-04-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-04-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-04-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -70.79 USD Expenses:Home:Phone 70.79 USD 2015-04-22 balance Assets:US:BofA:Checking 1285.92 USD 2015-04-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.06 USD Expenses:Home:Internet 80.06 USD 2015-05-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-05-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-05-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-05-17 balance Assets:US:BofA:Checking 905.82 USD 2015-05-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -60.53 USD Expenses:Home:Phone 60.53 USD 2015-05-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.91 USD Expenses:Home:Internet 79.91 USD 2015-06-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-06-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-06-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-06-16 balance Assets:US:BofA:Checking 473.06 USD 2015-06-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -67.78 USD Expenses:Home:Phone 67.78 USD 2015-06-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.95 USD Expenses:Home:Internet 79.95 USD 2015-07-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-07-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-07-07 balance Assets:US:BofA:Checking 622.53 USD 2015-07-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-07-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -66.39 USD Expenses:Home:Phone 66.39 USD 2015-07-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.87 USD Expenses:Home:Internet 79.87 USD 2015-07-28 balance Assets:US:BofA:Checking 1204.34 USD 2015-08-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-08-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-08-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-08-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -52.57 USD Expenses:Home:Phone 52.57 USD 2015-08-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.07 USD Expenses:Home:Internet 80.07 USD 2015-08-23 balance Assets:US:BofA:Checking 3046.55 USD 2015-09-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-09-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-09-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-09-11 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -4500 USD Assets:US:ETrade:Cash 4500 USD 2015-09-20 balance Assets:US:BofA:Checking 485.30 USD 2015-09-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -63.29 USD Expenses:Home:Phone 63.29 USD 2015-09-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.10 USD Expenses:Home:Internet 80.10 USD 2015-10-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-10-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-10-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-10-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -60.18 USD Expenses:Home:Phone 60.18 USD 2015-10-20 balance Assets:US:BofA:Checking 2433.29 USD 2015-10-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.04 USD Expenses:Home:Internet 80.04 USD 2015-10-23 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -4000 USD Assets:US:ETrade:Cash 4000 USD 2015-11-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-11-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-11-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-11-13 balance Assets:US:BofA:Checking 466.04 USD 2015-11-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -69.84 USD Expenses:Home:Phone 69.84 USD 2015-11-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.00 USD Expenses:Home:Internet 80.00 USD 2015-12-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-12-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-12-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-12-13 balance Assets:US:BofA:Checking 2709.79 USD 2015-12-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -66.06 USD Expenses:Home:Phone 66.06 USD 2015-12-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.08 USD Expenses:Home:Internet 80.08 USD 2016-01-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-01-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-01-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-01-12 balance Assets:US:BofA:Checking 5059.48 USD 2016-01-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -66.75 USD Expenses:Home:Phone 66.75 USD 2016-01-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.98 USD Expenses:Home:Internet 79.98 USD 2016-02-03 balance Assets:US:BofA:Checking 7613.95 USD 2016-02-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-02-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-02-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-02-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -69.87 USD Expenses:Home:Phone 69.87 USD 2016-02-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.99 USD Expenses:Home:Internet 79.99 USD 2016-02-27 balance Assets:US:BofA:Checking 7070.30 USD 2016-03-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-03-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-03-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-03-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -54.35 USD Expenses:Home:Phone 54.35 USD 2016-03-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.17 USD Expenses:Home:Internet 80.17 USD 2016-03-24 balance Assets:US:BofA:Checking 5045.38 USD 2016-04-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-04-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-04-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-04-17 balance Assets:US:BofA:Checking 4190.64 USD 2016-04-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -65.82 USD Expenses:Home:Phone 65.82 USD 2016-04-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.95 USD Expenses:Home:Internet 79.95 USD 2016-05-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-05-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-05-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-05-12 balance Assets:US:BofA:Checking 3782.61 USD 2016-05-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -46.37 USD Expenses:Home:Phone 46.37 USD 2016-05-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.94 USD Expenses:Home:Internet 79.94 USD 2016-06-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-06-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-06-07 balance Assets:US:BofA:Checking 3953.50 USD 2016-06-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-06-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -49.14 USD Expenses:Home:Phone 49.14 USD 2016-06-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.98 USD Expenses:Home:Internet 79.98 USD 2016-06-30 balance Assets:US:BofA:Checking 4493.60 USD 2016-07-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-07-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-07-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-07-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -65.42 USD Expenses:Home:Phone 65.42 USD 2016-07-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.01 USD Expenses:Home:Internet 80.01 USD 2016-07-24 balance Assets:US:BofA:Checking 3939.57 USD 2016-08-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-08-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-08-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-08-12 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -4000 USD Assets:US:ETrade:Cash 4000 USD 2016-08-16 balance Assets:US:BofA:Checking 749.97 USD 2016-08-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -56.96 USD Expenses:Home:Phone 56.96 USD 2016-08-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.07 USD Expenses:Home:Internet 80.07 USD 2016-09-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-09-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-09-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-09-13 balance Assets:US:BofA:Checking 2779.73 USD 2016-09-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -85.42 USD Expenses:Home:Phone 85.42 USD 2016-09-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.93 USD Expenses:Home:Internet 79.93 USD 2016-10-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-10-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-10-07 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -4000 USD Assets:US:ETrade:Cash 4000 USD 2016-10-09 balance Assets:US:BofA:Checking 1311.58 USD 2016-10-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-10-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -45.61 USD Expenses:Home:Phone 45.61 USD 2016-10-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.02 USD Expenses:Home:Internet 80.02 USD 2016-10-31 balance Assets:US:BofA:Checking 2951.39 USD 2016-11-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-11-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-11-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-11-18 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -3000 USD Assets:US:ETrade:Cash 3000 USD 2016-11-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -60.80 USD Expenses:Home:Phone 60.80 USD 2016-11-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.92 USD Expenses:Home:Internet 79.92 USD 2016-11-30 balance Assets:US:BofA:Checking 1688.07 USD 2016-12-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-12-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-12-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-12-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -57.31 USD Expenses:Home:Phone 57.31 USD 2016-12-22 balance Assets:US:BofA:Checking 3560.92 USD 2016-12-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.01 USD Expenses:Home:Internet 80.01 USD 2017-01-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2017-01-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2017-01-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2017-01-12 balance Assets:US:BofA:Checking 2937.08 USD 2017-01-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -69.25 USD Expenses:Home:Phone 69.25 USD 2017-01-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.89 USD Expenses:Home:Internet 79.89 USD 2017-02-04 balance Assets:US:BofA:Checking 5489.14 USD 2017-02-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2017-02-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2017-02-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2017-02-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -59.02 USD Expenses:Home:Phone 59.02 USD 2017-02-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.05 USD Expenses:Home:Internet 80.05 USD 2017-03-04 balance Assets:US:BofA:Checking 5194.85 USD 2017-03-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2017-03-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2017-03-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2017-03-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -67.67 USD Expenses:Home:Phone 67.67 USD 2017-03-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.98 USD Expenses:Home:Internet 79.98 USD 2017-03-25 balance Assets:US:BofA:Checking 3898.70 USD 2017-04-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2017-04-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2017-04-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2017-04-16 balance Assets:US:BofA:Checking 2171.56 USD 2017-04-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -85.18 USD Expenses:Home:Phone 85.18 USD 2017-04-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.20 USD Expenses:Home:Internet 80.20 USD 2017-05-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2017-05-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2017-05-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2017-05-12 balance Assets:US:BofA:Checking 1645.91 USD 2017-05-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -55.68 USD Expenses:Home:Phone 55.68 USD 2017-05-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.92 USD Expenses:Home:Internet 79.92 USD 2017-06-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2017-06-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2017-06-08 balance Assets:US:BofA:Checking 1119.31 USD 2017-06-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2017-06-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -54.38 USD Expenses:Home:Phone 54.38 USD 2017-06-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.00 USD Expenses:Home:Internet 80.00 USD 2017-07-01 balance Assets:US:BofA:Checking 3621.13 USD 2017-07-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2017-07-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2017-07-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2017-07-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -54.74 USD Expenses:Home:Phone 54.74 USD 2017-07-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.95 USD Expenses:Home:Internet 79.95 USD 2017-07-31 balance Assets:US:BofA:Checking 3068.98 USD 2017-08-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2017-08-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2017-08-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2017-08-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -61.73 USD Expenses:Home:Phone 61.73 USD 2017-08-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.98 USD Expenses:Home:Internet 79.98 USD 2017-08-25 balance Assets:US:BofA:Checking 4789.72 USD 2017-08-25 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -4500 USD Assets:US:ETrade:Cash 4500 USD 2017-09-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD * Credit-Cards 1980-05-12 open Liabilities:US:Chase:Slate USD 2015-01-06 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -30.95 USD Expenses:Food:Restaurant 30.95 USD 2015-01-09 * "Kin Soy" "Eating out with work buddies" Liabilities:US:Chase:Slate -40.93 USD Expenses:Food:Restaurant 40.93 USD 2015-01-09 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -63.30 USD Expenses:Food:Groceries 63.30 USD 2015-01-11 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -20.83 USD Expenses:Food:Restaurant 20.83 USD 2015-01-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 156.01 USD Assets:US:BofA:Checking -156.01 USD 2015-01-14 * "Jewel of Morroco" "Eating out with Julie" Liabilities:US:Chase:Slate -23.83 USD Expenses:Food:Restaurant 23.83 USD 2015-01-14 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -74.30 USD Expenses:Food:Groceries 74.30 USD 2015-01-15 * "Kin Soy" "Eating out with Bill" Liabilities:US:Chase:Slate -52.99 USD Expenses:Food:Restaurant 52.99 USD 2015-01-18 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -21.59 USD Expenses:Food:Restaurant 21.59 USD 2015-01-22 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -35.70 USD Expenses:Food:Restaurant 35.70 USD 2015-01-24 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -70.65 USD Expenses:Food:Groceries 70.65 USD 2015-01-25 balance Liabilities:US:Chase:Slate -279.06 USD 2015-01-25 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -31.82 USD Expenses:Food:Restaurant 31.82 USD 2015-01-28 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-01-29 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -83.29 USD Expenses:Food:Restaurant 83.29 USD 2015-02-03 * "Kin Soy" "Eating out with work buddies" Liabilities:US:Chase:Slate -27.90 USD Expenses:Food:Restaurant 27.90 USD 2015-02-04 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -20.88 USD Expenses:Food:Restaurant 20.88 USD 2015-02-04 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -77.84 USD Expenses:Food:Groceries 77.84 USD 2015-02-07 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -25.20 USD Expenses:Food:Restaurant 25.20 USD 2015-02-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 694.42 USD Assets:US:BofA:Checking -694.42 USD 2015-02-12 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -28.43 USD Expenses:Food:Restaurant 28.43 USD 2015-02-14 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -30.17 USD Expenses:Food:Restaurant 30.17 USD 2015-02-17 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -80.80 USD Expenses:Food:Groceries 80.80 USD 2015-02-18 balance Liabilities:US:Chase:Slate -110.97 USD 2015-02-19 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -10.46 USD Expenses:Food:Restaurant 10.46 USD 2015-02-23 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -54.35 USD Expenses:Food:Restaurant 54.35 USD 2015-02-25 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-02-28 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -25.19 USD Expenses:Food:Restaurant 25.19 USD 2015-03-02 * "Chichipotle" "Eating out with Natasha" Liabilities:US:Chase:Slate -59.04 USD Expenses:Food:Restaurant 59.04 USD 2015-03-02 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -119.28 USD Expenses:Food:Groceries 119.28 USD 2015-03-07 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -32.40 USD Expenses:Food:Restaurant 32.40 USD 2015-03-09 * "Uncle Boons" "Eating out with work buddies" Liabilities:US:Chase:Slate -43.98 USD Expenses:Food:Restaurant 43.98 USD 2015-03-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 575.67 USD Assets:US:BofA:Checking -575.67 USD 2015-03-10 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -30.37 USD Expenses:Food:Restaurant 30.37 USD 2015-03-15 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -23.92 USD Expenses:Food:Restaurant 23.92 USD 2015-03-16 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -26.54 USD Expenses:Food:Restaurant 26.54 USD 2015-03-17 balance Liabilities:US:Chase:Slate -80.83 USD 2015-03-19 * "Kin Soy" "Eating out with work buddies" Liabilities:US:Chase:Slate -37.89 USD Expenses:Food:Restaurant 37.89 USD 2015-03-19 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -99.62 USD Expenses:Food:Groceries 99.62 USD 2015-03-21 * "Goba Goba" "Eating out " Liabilities:US:Chase:Slate -48.55 USD Expenses:Food:Restaurant 48.55 USD 2015-03-24 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -22.54 USD Expenses:Food:Restaurant 22.54 USD 2015-03-27 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-03-28 * "Jewel of Morroco" "Eating out with Julie" Liabilities:US:Chase:Slate -16.09 USD Expenses:Food:Restaurant 16.09 USD 2015-04-02 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -20.27 USD Expenses:Food:Restaurant 20.27 USD 2015-04-06 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -24.07 USD Expenses:Food:Restaurant 24.07 USD 2015-04-07 balance Liabilities:US:Chase:Slate -469.86 USD 2015-04-07 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -46.33 USD Expenses:Food:Groceries 46.33 USD 2015-04-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 564.37 USD Assets:US:BofA:Checking -564.37 USD 2015-04-10 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -48.18 USD Expenses:Food:Restaurant 48.18 USD 2015-04-11 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -34.99 USD Expenses:Food:Restaurant 34.99 USD 2015-04-15 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -20.60 USD Expenses:Food:Restaurant 20.60 USD 2015-04-20 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -28.67 USD Expenses:Food:Restaurant 28.67 USD 2015-04-22 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -94.28 USD Expenses:Food:Groceries 94.28 USD 2015-04-24 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -15.76 USD Expenses:Food:Restaurant 15.76 USD 2015-04-26 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-04-27 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -78.14 USD Expenses:Food:Groceries 78.14 USD 2015-04-29 balance Liabilities:US:Chase:Slate -392.44 USD 2015-04-29 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -56.34 USD Expenses:Food:Restaurant 56.34 USD 2015-05-01 * "Chichipotle" "Eating out with work buddies" Liabilities:US:Chase:Slate -34.49 USD Expenses:Food:Restaurant 34.49 USD 2015-05-06 * "Kin Soy" "Eating out with Julie" Liabilities:US:Chase:Slate -31.56 USD Expenses:Food:Restaurant 31.56 USD 2015-05-09 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -17.41 USD Expenses:Food:Restaurant 17.41 USD 2015-05-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 532.24 USD Assets:US:BofA:Checking -532.24 USD 2015-05-13 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -24.57 USD Expenses:Food:Restaurant 24.57 USD 2015-05-15 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -104.09 USD Expenses:Food:Groceries 104.09 USD 2015-05-18 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -35.08 USD Expenses:Food:Restaurant 35.08 USD 2015-05-22 * "Jewel of Morroco" "Eating out with Julie" Liabilities:US:Chase:Slate -50.74 USD Expenses:Food:Restaurant 50.74 USD 2015-05-24 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-05-26 * "Uncle Boons" "Eating out after work" Liabilities:US:Chase:Slate -19.20 USD Expenses:Food:Restaurant 19.20 USD 2015-05-27 balance Liabilities:US:Chase:Slate -353.68 USD 2015-05-31 * "Rose Flower" "Eating out " Liabilities:US:Chase:Slate -20.80 USD Expenses:Food:Restaurant 20.80 USD 2015-06-02 * "China Garden" "Eating out with work buddies" Liabilities:US:Chase:Slate -16.25 USD Expenses:Food:Restaurant 16.25 USD 2015-06-02 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -83.56 USD Expenses:Food:Groceries 83.56 USD 2015-06-04 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -17.42 USD Expenses:Food:Restaurant 17.42 USD 2015-06-06 * "Chichipotle" "Eating out with work buddies" Liabilities:US:Chase:Slate -15.37 USD Expenses:Food:Restaurant 15.37 USD 2015-06-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 524.52 USD Assets:US:BofA:Checking -524.52 USD 2015-06-11 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -17.44 USD Expenses:Food:Restaurant 17.44 USD 2015-06-15 * "China Garden" "Eating out with Julie" Liabilities:US:Chase:Slate -43.53 USD Expenses:Food:Restaurant 43.53 USD 2015-06-17 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -57.61 USD Expenses:Food:Groceries 57.61 USD 2015-06-18 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -23.70 USD Expenses:Food:Restaurant 23.70 USD 2015-06-21 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -25.45 USD Expenses:Food:Restaurant 25.45 USD 2015-06-21 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-06-24 balance Liabilities:US:Chase:Slate -270.29 USD 2015-06-26 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -47.19 USD Expenses:Food:Restaurant 47.19 USD 2015-06-27 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -32.68 USD Expenses:Food:Restaurant 32.68 USD 2015-06-29 * "Kin Soy" "Eating out with Bill" Liabilities:US:Chase:Slate -25.22 USD Expenses:Food:Restaurant 25.22 USD 2015-07-03 * "Rose Flower" "Eating out " Liabilities:US:Chase:Slate -15.33 USD Expenses:Food:Restaurant 15.33 USD 2015-07-04 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -24.63 USD Expenses:Food:Restaurant 24.63 USD 2015-07-05 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -86.50 USD Expenses:Food:Groceries 86.50 USD 2015-07-08 * "Chichipotle" "Eating out after work" Liabilities:US:Chase:Slate -19.07 USD Expenses:Food:Restaurant 19.07 USD 2015-07-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 557.53 USD Assets:US:BofA:Checking -557.53 USD 2015-07-13 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -36.62 USD Expenses:Food:Restaurant 36.62 USD 2015-07-15 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -16.94 USD Expenses:Food:Restaurant 16.94 USD 2015-07-16 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -108.91 USD Expenses:Food:Groceries 108.91 USD 2015-07-19 * "Rose Flower" "Eating out after work" Liabilities:US:Chase:Slate -26.81 USD Expenses:Food:Restaurant 26.81 USD 2015-07-20 balance Liabilities:US:Chase:Slate -152.66 USD 2015-07-21 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-07-22 * "Goba Goba" "Eating out alone" Liabilities:US:Chase:Slate -18.68 USD Expenses:Food:Restaurant 18.68 USD 2015-07-27 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -25.07 USD Expenses:Food:Restaurant 25.07 USD 2015-07-30 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -96.26 USD Expenses:Food:Groceries 96.26 USD 2015-08-01 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -18.50 USD Expenses:Food:Restaurant 18.50 USD 2015-08-03 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -47.57 USD Expenses:Food:Restaurant 47.57 USD 2015-08-08 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -19.68 USD Expenses:Food:Restaurant 19.68 USD 2015-08-09 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -35.50 USD Expenses:Food:Restaurant 35.50 USD 2015-08-10 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -123.43 USD Expenses:Food:Groceries 123.43 USD 2015-08-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 657.35 USD Assets:US:BofA:Checking -657.35 USD 2015-08-12 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -24.65 USD Expenses:Food:Restaurant 24.65 USD 2015-08-14 balance Liabilities:US:Chase:Slate -24.65 USD 2015-08-15 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -50.55 USD Expenses:Food:Restaurant 50.55 USD 2015-08-16 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -56.07 USD Expenses:Food:Restaurant 56.07 USD 2015-08-17 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -29.23 USD Expenses:Food:Restaurant 29.23 USD 2015-08-18 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-08-20 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -38.11 USD Expenses:Food:Restaurant 38.11 USD 2015-08-24 * "Jewel of Morroco" "Eating out with Natasha" Liabilities:US:Chase:Slate -27.12 USD Expenses:Food:Restaurant 27.12 USD 2015-08-27 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -18.02 USD Expenses:Food:Restaurant 18.02 USD 2015-08-30 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -32.53 USD Expenses:Food:Restaurant 32.53 USD 2015-08-30 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -58.65 USD Expenses:Food:Groceries 58.65 USD 2015-09-03 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -21.21 USD Expenses:Food:Restaurant 21.21 USD 2015-09-05 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -99.08 USD Expenses:Food:Groceries 99.08 USD 2015-09-06 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -57.06 USD Expenses:Food:Restaurant 57.06 USD 2015-09-07 balance Liabilities:US:Chase:Slate -632.28 USD 2015-09-09 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -42.54 USD Expenses:Food:Restaurant 42.54 USD 2015-09-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 693.45 USD Assets:US:BofA:Checking -693.45 USD 2015-09-13 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -18.63 USD Expenses:Food:Restaurant 18.63 USD 2015-09-15 * "Cafe Modagor" "Eating out with Bill" Liabilities:US:Chase:Slate -27.24 USD Expenses:Food:Restaurant 27.24 USD 2015-09-16 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-09-18 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -74.35 USD Expenses:Food:Groceries 74.35 USD 2015-09-20 * "Kin Soy" "Eating out with Julie" Liabilities:US:Chase:Slate -28.24 USD Expenses:Food:Restaurant 28.24 USD 2015-09-23 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -19.38 USD Expenses:Food:Restaurant 19.38 USD 2015-09-27 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -20.59 USD Expenses:Food:Restaurant 20.59 USD 2015-09-30 * "Cafe Modagor" "Eating out with Bill" Liabilities:US:Chase:Slate -18.85 USD Expenses:Food:Restaurant 18.85 USD 2015-10-01 balance Liabilities:US:Chase:Slate -308.65 USD 2015-10-04 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -50.36 USD Expenses:Food:Restaurant 50.36 USD 2015-10-07 * "Uncle Boons" "Eating out " Liabilities:US:Chase:Slate -15.28 USD Expenses:Food:Restaurant 15.28 USD 2015-10-08 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -71.43 USD Expenses:Food:Groceries 71.43 USD 2015-10-09 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -34.92 USD Expenses:Food:Restaurant 34.92 USD 2015-10-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 480.64 USD Assets:US:BofA:Checking -480.64 USD 2015-10-13 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -18.64 USD Expenses:Food:Restaurant 18.64 USD 2015-10-15 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -28.06 USD Expenses:Food:Restaurant 28.06 USD 2015-10-17 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -22.41 USD Expenses:Food:Restaurant 22.41 USD 2015-10-19 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-10-20 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -27.17 USD Expenses:Food:Restaurant 27.17 USD 2015-10-24 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -28.81 USD Expenses:Food:Restaurant 28.81 USD 2015-10-26 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -54.04 USD Expenses:Food:Groceries 54.04 USD 2015-10-27 balance Liabilities:US:Chase:Slate -299.13 USD 2015-10-27 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -29.08 USD Expenses:Food:Restaurant 29.08 USD 2015-10-28 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -25.91 USD Expenses:Food:Restaurant 25.91 USD 2015-11-02 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -43.94 USD Expenses:Food:Restaurant 43.94 USD 2015-11-04 * "Uncle Boons" "Eating out " Liabilities:US:Chase:Slate -37.41 USD Expenses:Food:Restaurant 37.41 USD 2015-11-06 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -16.45 USD Expenses:Food:Restaurant 16.45 USD 2015-11-07 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -39.94 USD Expenses:Food:Restaurant 39.94 USD 2015-11-08 event "location" "Boston" 2015-11-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 519.41 USD Assets:US:BofA:Checking -519.41 USD 2015-11-10 * "Starbucks" "" #trip-boston-2015 Liabilities:US:Chase:Slate -5.20 USD Expenses:Food:Coffee 5.20 USD 2015-11-11 * "Legal Seafood" "" #trip-boston-2015 Liabilities:US:Chase:Slate -42.07 USD Expenses:Food:Restaurant 42.07 USD 2015-11-12 * "Franklin Cafe" "" #trip-boston-2015 Liabilities:US:Chase:Slate -33.85 USD Expenses:Food:Restaurant 33.85 USD 2015-11-12 * "Starbucks" "" #trip-boston-2015 Liabilities:US:Chase:Slate -6.18 USD Expenses:Food:Coffee 6.18 USD 2015-11-14 * "Starbucks" "" #trip-boston-2015 Liabilities:US:Chase:Slate -6.84 USD Expenses:Food:Coffee 6.84 USD 2015-11-15 * "Franklin Cafe" "" #trip-boston-2015 Liabilities:US:Chase:Slate -29.03 USD Expenses:Food:Restaurant 29.03 USD 2015-11-18 * "Giacomo's Restaurant" "" #trip-boston-2015 Liabilities:US:Chase:Slate -36.63 USD Expenses:Food:Restaurant 36.63 USD 2015-11-18 * "Legal Seafood" "" #trip-boston-2015 Liabilities:US:Chase:Slate -36.12 USD Expenses:Food:Restaurant 36.12 USD 2015-11-19 * "Legal Seafood" "" #trip-boston-2015 Liabilities:US:Chase:Slate -38.73 USD Expenses:Food:Restaurant 38.73 USD 2015-11-20 * "Legal Seafood" "" #trip-boston-2015 Liabilities:US:Chase:Slate -33.83 USD Expenses:Food:Restaurant 33.83 USD 2015-11-21 * "Giacomo's Restaurant" "" #trip-boston-2015 Liabilities:US:Chase:Slate -47.91 USD Expenses:Food:Restaurant 47.91 USD 2015-11-22 * "Legal Seafood" "" #trip-boston-2015 Liabilities:US:Chase:Slate -36.28 USD Expenses:Food:Restaurant 36.28 USD 2015-11-23 * "Legal Seafood" "" #trip-boston-2015 Liabilities:US:Chase:Slate -28.89 USD Expenses:Food:Restaurant 28.89 USD 2015-11-23 * "Franklin Cafe" "" #trip-boston-2015 Liabilities:US:Chase:Slate -29.13 USD Expenses:Food:Restaurant 29.13 USD 2015-11-24 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -27.55 USD Expenses:Food:Restaurant 27.55 USD 2015-11-24 * "Franklin Cafe" "" #trip-boston-2015 Liabilities:US:Chase:Slate -31.75 USD Expenses:Food:Restaurant 31.75 USD 2015-11-24 * "Consume vacation days" Assets:US:BayBook:Vacation -128 VACHR Expenses:Vacation 128 VACHR 2015-11-24 event "location" "New Metropolis" 2015-11-26 balance Liabilities:US:Chase:Slate -442.44 USD 2015-11-27 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -38.42 USD Expenses:Food:Restaurant 38.42 USD 2015-11-28 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -19.18 USD Expenses:Food:Restaurant 19.18 USD 2015-11-29 * "China Garden" "Eating out " Liabilities:US:Chase:Slate -26.12 USD Expenses:Food:Restaurant 26.12 USD 2015-12-01 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -16.77 USD Expenses:Food:Restaurant 16.77 USD 2015-12-04 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -85.32 USD Expenses:Food:Groceries 85.32 USD 2015-12-05 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -23.31 USD Expenses:Food:Restaurant 23.31 USD 2015-12-06 * "Goba Goba" "Eating out " Liabilities:US:Chase:Slate -37.79 USD Expenses:Food:Restaurant 37.79 USD 2015-12-10 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -30.16 USD Expenses:Food:Restaurant 30.16 USD 2015-12-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 277.07 USD Assets:US:BofA:Checking -277.07 USD 2015-12-11 * "Uncle Boons" "Eating out " Liabilities:US:Chase:Slate -20.01 USD Expenses:Food:Restaurant 20.01 USD 2015-12-12 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -24.68 USD Expenses:Food:Restaurant 24.68 USD 2015-12-16 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-12-17 balance Liabilities:US:Chase:Slate -607.13 USD 2015-12-17 * "Kin Soy" "Eating out with work buddies" Liabilities:US:Chase:Slate -23.73 USD Expenses:Food:Restaurant 23.73 USD 2015-12-18 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -67.89 USD Expenses:Food:Groceries 67.89 USD 2015-12-22 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -11.13 USD Expenses:Food:Restaurant 11.13 USD 2015-12-26 * "Uncle Boons" "Eating out with Bill" Liabilities:US:Chase:Slate -16.10 USD Expenses:Food:Restaurant 16.10 USD 2015-12-30 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -77.39 USD Expenses:Food:Groceries 77.39 USD 2015-12-31 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -54.30 USD Expenses:Food:Restaurant 54.30 USD 2016-01-01 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -43.85 USD Expenses:Food:Restaurant 43.85 USD 2016-01-04 * "Uncle Boons" "Eating out with Bill" Liabilities:US:Chase:Slate -7.58 USD Expenses:Food:Restaurant 7.58 USD 2016-01-04 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -109.05 USD Expenses:Food:Groceries 109.05 USD 2016-01-09 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -23.85 USD Expenses:Food:Restaurant 23.85 USD 2016-01-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 699.45 USD Assets:US:BofA:Checking -699.45 USD 2016-01-13 balance Liabilities:US:Chase:Slate -342.55 USD 2016-01-14 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -99.89 USD Expenses:Food:Restaurant 99.89 USD 2016-01-17 * "Rose Flower" "Eating out after work" Liabilities:US:Chase:Slate -25.45 USD Expenses:Food:Restaurant 25.45 USD 2016-01-17 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2016-01-19 * "Kin Soy" "Eating out with work buddies" Liabilities:US:Chase:Slate -35.78 USD Expenses:Food:Restaurant 35.78 USD 2016-01-21 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -106.25 USD Expenses:Food:Groceries 106.25 USD 2016-01-22 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -40.35 USD Expenses:Food:Restaurant 40.35 USD 2016-01-27 * "Uncle Boons" "Eating out after work" Liabilities:US:Chase:Slate -38.41 USD Expenses:Food:Restaurant 38.41 USD 2016-01-31 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -36.04 USD Expenses:Food:Restaurant 36.04 USD 2016-02-01 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -65.31 USD Expenses:Food:Restaurant 65.31 USD 2016-02-04 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -103.15 USD Expenses:Food:Groceries 103.15 USD 2016-02-05 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -31.73 USD Expenses:Food:Restaurant 31.73 USD 2016-02-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 625.99 USD Assets:US:BofA:Checking -625.99 USD 2016-02-08 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -23.52 USD Expenses:Food:Restaurant 23.52 USD 2016-02-09 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -108.39 USD Expenses:Food:Groceries 108.39 USD 2016-02-10 balance Liabilities:US:Chase:Slate -550.83 USD 2016-02-12 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -28.41 USD Expenses:Food:Restaurant 28.41 USD 2016-02-13 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -33.95 USD Expenses:Food:Restaurant 33.95 USD 2016-02-15 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -31.55 USD Expenses:Food:Restaurant 31.55 USD 2016-02-17 * "Uncle Boons" "Eating out with Julie" Liabilities:US:Chase:Slate -39.86 USD Expenses:Food:Restaurant 39.86 USD 2016-02-19 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2016-02-20 * "Goba Goba" "Eating out alone" Liabilities:US:Chase:Slate -26.37 USD Expenses:Food:Restaurant 26.37 USD 2016-02-22 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -56.03 USD Expenses:Food:Restaurant 56.03 USD 2016-02-27 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -20.78 USD Expenses:Food:Restaurant 20.78 USD 2016-02-27 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -74.10 USD Expenses:Food:Groceries 74.10 USD 2016-02-28 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -50.34 USD Expenses:Food:Restaurant 50.34 USD 2016-02-29 * "Jewel of Morroco" "Eating out with work buddies" Liabilities:US:Chase:Slate -21.19 USD Expenses:Food:Restaurant 21.19 USD 2016-03-04 * "Goba Goba" "Eating out " Liabilities:US:Chase:Slate -20.48 USD Expenses:Food:Restaurant 20.48 USD 2016-03-06 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -54.45 USD Expenses:Food:Groceries 54.45 USD 2016-03-08 balance Liabilities:US:Chase:Slate -1128.34 USD 2016-03-08 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -24.37 USD Expenses:Food:Restaurant 24.37 USD 2016-03-09 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -32.15 USD Expenses:Food:Restaurant 32.15 USD 2016-03-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 772.00 USD Assets:US:BofA:Checking -772.00 USD 2016-03-12 * "Rose Flower" "Eating out after work" Liabilities:US:Chase:Slate -29.58 USD Expenses:Food:Restaurant 29.58 USD 2016-03-14 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -55.30 USD Expenses:Food:Restaurant 55.30 USD 2016-03-15 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -108.77 USD Expenses:Food:Groceries 108.77 USD 2016-03-17 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -46.77 USD Expenses:Food:Restaurant 46.77 USD 2016-03-18 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -27.78 USD Expenses:Food:Restaurant 27.78 USD 2016-03-19 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2016-03-23 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -22.43 USD Expenses:Food:Restaurant 22.43 USD 2016-03-26 event "location" "Los Angeles" 2016-03-27 * "Mr. Marcel" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -45.84 USD Expenses:Food:Restaurant 45.84 USD 2016-03-27 * "Dupar's" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -28.45 USD Expenses:Food:Restaurant 28.45 USD 2016-03-28 * "Banana Leaf" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -26.08 USD Expenses:Food:Restaurant 26.08 USD 2016-03-28 * "E.B.'s Beer and Wine" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -10.61 USD Expenses:Food:Alcohol 10.61 USD 2016-03-29 * "Mr. Marcel" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -30.76 USD Expenses:Food:Restaurant 30.76 USD 2016-03-29 * "Starbucks" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -6.50 USD Expenses:Food:Coffee 6.50 USD 2016-03-30 * "Banana Leaf" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -24.43 USD Expenses:Food:Restaurant 24.43 USD 2016-03-30 * "Chipotle" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -17.71 USD Expenses:Food:Restaurant 17.71 USD 2016-03-30 * "E.B.'s Beer and Wine" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -10.70 USD Expenses:Food:Alcohol 10.70 USD 2016-03-31 * "Mr. Marcel" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -35.02 USD Expenses:Food:Restaurant 35.02 USD 2016-03-31 * "Banana Leaf" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -24.94 USD Expenses:Food:Restaurant 24.94 USD 2016-03-31 * "Starbucks" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -5.86 USD Expenses:Food:Coffee 5.86 USD 2016-04-01 * "Chipotle" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -17.49 USD Expenses:Food:Restaurant 17.49 USD 2016-04-01 * "E.B.'s Beer and Wine" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -9.65 USD Expenses:Food:Alcohol 9.65 USD 2016-04-02 * "Mr. Marcel" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -47.80 USD Expenses:Food:Restaurant 47.80 USD 2016-04-02 * "Dupar's" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -29.87 USD Expenses:Food:Restaurant 29.87 USD 2016-04-02 * "Pampas Grill" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -23.04 USD Expenses:Food:Restaurant 23.04 USD 2016-04-03 * "Banana Leaf" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -24.43 USD Expenses:Food:Restaurant 24.43 USD 2016-04-03 * "Starbucks" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -5.66 USD Expenses:Food:Coffee 5.66 USD 2016-04-04 * "E.B.'s Beer and Wine" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -10.53 USD Expenses:Food:Alcohol 10.53 USD 2016-04-05 * "E.B.'s Beer and Wine" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -9.56 USD Expenses:Food:Alcohol 9.56 USD 2016-04-06 * "Chipotle" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -18.77 USD Expenses:Food:Restaurant 18.77 USD 2016-04-07 balance Liabilities:US:Chase:Slate -1287.19 USD 2016-04-07 * "Banana Leaf" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -24.34 USD Expenses:Food:Restaurant 24.34 USD 2016-04-07 * "Dupar's" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -29.41 USD Expenses:Food:Restaurant 29.41 USD 2016-04-07 * "Starbucks" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -5.85 USD Expenses:Food:Coffee 5.85 USD 2016-04-07 * "E.B.'s Beer and Wine" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -9.44 USD Expenses:Food:Alcohol 9.44 USD 2016-04-08 * "Pampas Grill" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -26.67 USD Expenses:Food:Restaurant 26.67 USD 2016-04-08 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 428.47 USD Assets:US:BofA:Checking -428.47 USD 2016-04-09 * "Mr. Marcel" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -52.69 USD Expenses:Food:Restaurant 52.69 USD 2016-04-09 * "Banana Leaf" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -26.11 USD Expenses:Food:Restaurant 26.11 USD 2016-04-09 * "Dupar's" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -28.77 USD Expenses:Food:Restaurant 28.77 USD 2016-04-10 * "Mr. Marcel" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -62.26 USD Expenses:Food:Restaurant 62.26 USD 2016-04-10 * "Dupar's" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -27.39 USD Expenses:Food:Restaurant 27.39 USD 2016-04-10 * "Starbucks" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -6.37 USD Expenses:Food:Coffee 6.37 USD 2016-04-11 * "Dupar's" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -32.21 USD Expenses:Food:Restaurant 32.21 USD 2016-04-11 * "Pampas Grill" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -27.21 USD Expenses:Food:Restaurant 27.21 USD 2016-04-11 * "Starbucks" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -5.34 USD Expenses:Food:Coffee 5.34 USD 2016-04-12 * "Mr. Marcel" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -47.26 USD Expenses:Food:Restaurant 47.26 USD 2016-04-12 * "Banana Leaf" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -26.03 USD Expenses:Food:Restaurant 26.03 USD 2016-04-12 * "E.B.'s Beer and Wine" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -10.61 USD Expenses:Food:Alcohol 10.61 USD 2016-04-13 * "Mr. Marcel" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -30.11 USD Expenses:Food:Restaurant 30.11 USD 2016-04-13 * "Dupar's" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -27.10 USD Expenses:Food:Restaurant 27.10 USD 2016-04-15 * "Pampas Grill" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -20.09 USD Expenses:Food:Restaurant 20.09 USD 2016-04-15 * "Starbucks" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -6.38 USD Expenses:Food:Coffee 6.38 USD 2016-04-16 * "Mr. Marcel" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -39.15 USD Expenses:Food:Restaurant 39.15 USD 2016-04-16 * "Chipotle" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -15.23 USD Expenses:Food:Restaurant 15.23 USD 2016-04-16 * "Starbucks" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -6.25 USD Expenses:Food:Coffee 6.25 USD 2016-04-17 * "Chichipotle" "Eating out with Natasha" Liabilities:US:Chase:Slate -47.42 USD Expenses:Food:Restaurant 47.42 USD 2016-04-17 * "Pampas Grill" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -15.90 USD Expenses:Food:Restaurant 15.90 USD 2016-04-17 * "E.B.'s Beer and Wine" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -8.98 USD Expenses:Food:Alcohol 8.98 USD 2016-04-17 * "Consume vacation days" Assets:US:BayBook:Vacation -176 VACHR Expenses:Vacation 176 VACHR 2016-04-17 event "location" "New Metropolis" 2016-04-18 * "Cafe Modagor" "Eating out with Bill" Liabilities:US:Chase:Slate -34.47 USD Expenses:Food:Restaurant 34.47 USD 2016-04-19 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -101.08 USD Expenses:Food:Groceries 101.08 USD 2016-04-22 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -23.27 USD Expenses:Food:Restaurant 23.27 USD 2016-04-26 * "Uncle Boons" "Eating out " Liabilities:US:Chase:Slate -53.87 USD Expenses:Food:Restaurant 53.87 USD 2016-04-28 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -17.40 USD Expenses:Food:Restaurant 17.40 USD 2016-05-02 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -21.43 USD Expenses:Food:Restaurant 21.43 USD 2016-05-04 balance Liabilities:US:Chase:Slate -1774.81 USD 2016-05-05 * "China Garden" "Eating out with work buddies" Liabilities:US:Chase:Slate -28.32 USD Expenses:Food:Restaurant 28.32 USD 2016-05-08 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -32.49 USD Expenses:Food:Restaurant 32.49 USD 2016-05-09 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -110.81 USD Expenses:Food:Groceries 110.81 USD 2016-05-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 494.46 USD Assets:US:BofA:Checking -494.46 USD 2016-05-13 * "Uncle Boons" "Eating out with Bill" Liabilities:US:Chase:Slate -71.32 USD Expenses:Food:Restaurant 71.32 USD 2016-05-16 * "Chichipotle" "Eating out after work" Liabilities:US:Chase:Slate -21.98 USD Expenses:Food:Restaurant 21.98 USD 2016-05-16 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2016-05-19 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -100.79 USD Expenses:Food:Groceries 100.79 USD 2016-05-21 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -31.29 USD Expenses:Food:Restaurant 31.29 USD 2016-05-25 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -70.43 USD Expenses:Food:Restaurant 70.43 USD 2016-05-28 * "Kin Soy" "Eating out with work buddies" Liabilities:US:Chase:Slate -23.23 USD Expenses:Food:Restaurant 23.23 USD 2016-05-29 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -19.33 USD Expenses:Food:Restaurant 19.33 USD 2016-05-31 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -138.30 USD Expenses:Food:Groceries 138.30 USD 2016-06-02 balance Liabilities:US:Chase:Slate -2048.64 USD 2016-06-02 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -29.59 USD Expenses:Food:Restaurant 29.59 USD 2016-06-06 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -37.39 USD Expenses:Food:Restaurant 37.39 USD 2016-06-08 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 616.38 USD Assets:US:BofA:Checking -616.38 USD 2016-06-10 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -24.05 USD Expenses:Food:Restaurant 24.05 USD 2016-06-14 * "Uncle Boons" "Eating out with Julie" Liabilities:US:Chase:Slate -33.64 USD Expenses:Food:Restaurant 33.64 USD 2016-06-15 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2016-06-18 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -33.15 USD Expenses:Food:Restaurant 33.15 USD 2016-06-18 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -69.15 USD Expenses:Food:Groceries 69.15 USD 2016-06-22 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -30.26 USD Expenses:Food:Restaurant 30.26 USD 2016-06-23 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -55.49 USD Expenses:Food:Restaurant 55.49 USD 2016-06-24 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -46.83 USD Expenses:Food:Restaurant 46.83 USD 2016-06-25 balance Liabilities:US:Chase:Slate -1911.81 USD 2016-06-26 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -27.39 USD Expenses:Food:Restaurant 27.39 USD 2016-06-28 * "Goba Goba" "Eating out alone" Liabilities:US:Chase:Slate -25.64 USD Expenses:Food:Restaurant 25.64 USD 2016-07-01 * "Jewel of Morroco" "Eating out with work buddies" Liabilities:US:Chase:Slate -19.28 USD Expenses:Food:Restaurant 19.28 USD 2016-07-02 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -31.50 USD Expenses:Food:Restaurant 31.50 USD 2016-07-04 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -72.67 USD Expenses:Food:Groceries 72.67 USD 2016-07-06 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -17.94 USD Expenses:Food:Restaurant 17.94 USD 2016-07-07 * "China Garden" "Eating out with Julie" Liabilities:US:Chase:Slate -40.37 USD Expenses:Food:Restaurant 40.37 USD 2016-07-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 640.80 USD Assets:US:BofA:Checking -640.80 USD 2016-07-11 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -17.49 USD Expenses:Food:Restaurant 17.49 USD 2016-07-16 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -41.19 USD Expenses:Food:Restaurant 41.19 USD 2016-07-16 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2016-07-19 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -69.58 USD Expenses:Food:Restaurant 69.58 USD 2016-07-23 * "Goba Goba" "Eating out " Liabilities:US:Chase:Slate -12.52 USD Expenses:Food:Restaurant 12.52 USD 2016-07-23 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -89.97 USD Expenses:Food:Groceries 89.97 USD 2016-07-24 balance Liabilities:US:Chase:Slate -1856.55 USD 2016-07-26 * "China Garden" "Eating out with Julie" Liabilities:US:Chase:Slate -50.66 USD Expenses:Food:Restaurant 50.66 USD 2016-07-29 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -53.12 USD Expenses:Food:Restaurant 53.12 USD 2016-08-02 * "Cafe Modagor" "Eating out with Julie" Liabilities:US:Chase:Slate -16.94 USD Expenses:Food:Restaurant 16.94 USD 2016-08-04 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -22.61 USD Expenses:Food:Restaurant 22.61 USD 2016-08-07 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -80.76 USD Expenses:Food:Groceries 80.76 USD 2016-08-08 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -30.71 USD Expenses:Food:Restaurant 30.71 USD 2016-08-09 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -33.74 USD Expenses:Food:Restaurant 33.74 USD 2016-08-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 621.80 USD Assets:US:BofA:Checking -621.80 USD 2016-08-14 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -9.56 USD Expenses:Food:Restaurant 9.56 USD 2016-08-16 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2016-08-17 * "Kin Soy" "Eating out with Bill" Liabilities:US:Chase:Slate -38.97 USD Expenses:Food:Restaurant 38.97 USD 2016-08-22 * "Jewel of Morroco" "Eating out with work buddies" Liabilities:US:Chase:Slate -12.26 USD Expenses:Food:Restaurant 12.26 USD 2016-08-23 balance Liabilities:US:Chase:Slate -1704.08 USD 2016-08-27 * "Uncle Boons" "Eating out with Julie" Liabilities:US:Chase:Slate -27.52 USD Expenses:Food:Restaurant 27.52 USD 2016-08-27 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -97.60 USD Expenses:Food:Groceries 97.60 USD 2016-08-28 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -44.53 USD Expenses:Food:Restaurant 44.53 USD 2016-09-01 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -31.40 USD Expenses:Food:Restaurant 31.40 USD 2016-09-06 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -19.43 USD Expenses:Food:Restaurant 19.43 USD 2016-09-10 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -64.14 USD Expenses:Food:Restaurant 64.14 USD 2016-09-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 465.41 USD Assets:US:BofA:Checking -465.41 USD 2016-09-11 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -80.33 USD Expenses:Food:Groceries 80.33 USD 2016-09-14 * "Kin Soy" "Eating out with work buddies" Liabilities:US:Chase:Slate -31.59 USD Expenses:Food:Restaurant 31.59 USD 2016-09-15 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2016-09-16 balance Liabilities:US:Chase:Slate -1755.21 USD 2016-09-18 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -22.62 USD Expenses:Food:Restaurant 22.62 USD 2016-09-19 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -31.00 USD Expenses:Food:Restaurant 31.00 USD 2016-09-22 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -19.74 USD Expenses:Food:Restaurant 19.74 USD 2016-09-22 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -121.28 USD Expenses:Food:Groceries 121.28 USD 2016-09-25 * "Kin Soy" "Eating out with work buddies" Liabilities:US:Chase:Slate -22.75 USD Expenses:Food:Restaurant 22.75 USD 2016-09-29 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -47.21 USD Expenses:Food:Restaurant 47.21 USD 2016-10-02 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -62.63 USD Expenses:Food:Groceries 62.63 USD 2016-10-04 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -66.23 USD Expenses:Food:Restaurant 66.23 USD 2016-10-06 balance Liabilities:US:Chase:Slate -2148.67 USD 2016-10-06 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -26.32 USD Expenses:Food:Restaurant 26.32 USD 2016-10-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 720.16 USD Assets:US:BofA:Checking -720.16 USD 2016-10-11 * "Jewel of Morroco" "Eating out with Julie" Liabilities:US:Chase:Slate -68.46 USD Expenses:Food:Restaurant 68.46 USD 2016-10-12 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -83.44 USD Expenses:Food:Restaurant 83.44 USD 2016-10-13 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2016-10-14 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -21.78 USD Expenses:Food:Restaurant 21.78 USD 2016-10-15 * "Cafe Modagor" "Eating out with Julie" Liabilities:US:Chase:Slate -46.22 USD Expenses:Food:Restaurant 46.22 USD 2016-10-16 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -26.26 USD Expenses:Food:Restaurant 26.26 USD 2016-10-16 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -103.65 USD Expenses:Food:Groceries 103.65 USD 2016-10-19 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -51.41 USD Expenses:Food:Restaurant 51.41 USD 2016-10-20 * "Uncle Boons" "Eating out " Liabilities:US:Chase:Slate -38.32 USD Expenses:Food:Restaurant 38.32 USD 2016-10-23 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -47.00 USD Expenses:Food:Restaurant 47.00 USD 2016-10-28 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -56.25 USD Expenses:Food:Restaurant 56.25 USD 2016-10-30 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -69.04 USD Expenses:Food:Groceries 69.04 USD 2016-11-01 balance Liabilities:US:Chase:Slate -2186.66 USD 2016-11-01 * "Uncle Boons" "Eating out after work" Liabilities:US:Chase:Slate -33.66 USD Expenses:Food:Restaurant 33.66 USD 2016-11-04 * "Uncle Boons" "Eating out with work buddies" Liabilities:US:Chase:Slate -35.62 USD Expenses:Food:Restaurant 35.62 USD 2016-11-07 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -22.15 USD Expenses:Food:Restaurant 22.15 USD 2016-11-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 754.80 USD Assets:US:BofA:Checking -754.80 USD 2016-11-08 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -39.27 USD Expenses:Food:Restaurant 39.27 USD 2016-11-12 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2016-11-13 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -35.51 USD Expenses:Food:Restaurant 35.51 USD 2016-11-14 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -82.80 USD Expenses:Food:Groceries 82.80 USD 2016-11-17 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -41.15 USD Expenses:Food:Restaurant 41.15 USD 2016-11-20 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -30.69 USD Expenses:Food:Restaurant 30.69 USD 2016-11-21 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -53.93 USD Expenses:Food:Restaurant 53.93 USD 2016-11-22 balance Liabilities:US:Chase:Slate -1926.64 USD 2016-11-24 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -44.74 USD Expenses:Food:Restaurant 44.74 USD 2016-11-25 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -77.99 USD Expenses:Food:Groceries 77.99 USD 2016-11-28 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -46.68 USD Expenses:Food:Restaurant 46.68 USD 2016-12-01 * "China Garden" "Eating out with Bill" Liabilities:US:Chase:Slate -57.49 USD Expenses:Food:Restaurant 57.49 USD 2016-12-03 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -39.41 USD Expenses:Food:Restaurant 39.41 USD 2016-12-06 * "China Garden" "Eating out " Liabilities:US:Chase:Slate -48.34 USD Expenses:Food:Restaurant 48.34 USD 2016-12-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 740.50 USD Assets:US:BofA:Checking -740.50 USD 2016-12-10 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -22.50 USD Expenses:Food:Restaurant 22.50 USD 2016-12-11 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -71.35 USD Expenses:Food:Groceries 71.35 USD 2016-12-12 * "China Garden" "Eating out with Bill" Liabilities:US:Chase:Slate -14.65 USD Expenses:Food:Restaurant 14.65 USD 2016-12-12 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2016-12-15 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -65.11 USD Expenses:Food:Restaurant 65.11 USD 2016-12-17 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -18.63 USD Expenses:Food:Restaurant 18.63 USD 2016-12-19 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -45.01 USD Expenses:Food:Restaurant 45.01 USD 2016-12-21 * "Kin Soy" "Eating out after work" Liabilities:US:Chase:Slate -35.40 USD Expenses:Food:Restaurant 35.40 USD 2016-12-21 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -62.68 USD Expenses:Food:Groceries 62.68 USD 2016-12-22 balance Liabilities:US:Chase:Slate -1956.12 USD 2016-12-25 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -36.15 USD Expenses:Food:Restaurant 36.15 USD 2016-12-28 * "Goba Goba" "Eating out " Liabilities:US:Chase:Slate -24.06 USD Expenses:Food:Restaurant 24.06 USD 2016-12-31 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -66.99 USD Expenses:Food:Restaurant 66.99 USD 2017-01-01 * "Goba Goba" "Eating out alone" Liabilities:US:Chase:Slate -23.43 USD Expenses:Food:Restaurant 23.43 USD 2017-01-04 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -154.56 USD Expenses:Food:Groceries 154.56 USD 2017-01-06 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -28.87 USD Expenses:Food:Restaurant 28.87 USD 2017-01-08 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -53.40 USD Expenses:Food:Restaurant 53.40 USD 2017-01-09 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -86.68 USD Expenses:Food:Groceries 86.68 USD 2017-01-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 906.97 USD Assets:US:BofA:Checking -906.97 USD 2017-01-10 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2017-01-11 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -32.34 USD Expenses:Food:Restaurant 32.34 USD 2017-01-12 balance Liabilities:US:Chase:Slate -1675.63 USD 2017-01-12 event "location" "Boston" 2017-01-13 * "Legal Seafood" "" #trip-boston-2017 Liabilities:US:Chase:Slate -24.43 USD Expenses:Food:Restaurant 24.43 USD 2017-01-13 * "Starbucks" "" #trip-boston-2017 Liabilities:US:Chase:Slate -6.90 USD Expenses:Food:Coffee 6.90 USD 2017-01-14 * "Legal Seafood" "" #trip-boston-2017 Liabilities:US:Chase:Slate -43.45 USD Expenses:Food:Restaurant 43.45 USD 2017-01-16 * "Giacomo's Restaurant" "" #trip-boston-2017 Liabilities:US:Chase:Slate -31.57 USD Expenses:Food:Restaurant 31.57 USD 2017-01-17 * "Giacomo's Restaurant" "" #trip-boston-2017 Liabilities:US:Chase:Slate -41.39 USD Expenses:Food:Restaurant 41.39 USD 2017-01-17 * "Franklin Cafe" "" #trip-boston-2017 Liabilities:US:Chase:Slate -26.62 USD Expenses:Food:Restaurant 26.62 USD 2017-01-18 * "Giacomo's Restaurant" "" #trip-boston-2017 Liabilities:US:Chase:Slate -47.56 USD Expenses:Food:Restaurant 47.56 USD 2017-01-18 * "Legal Seafood" "" #trip-boston-2017 Liabilities:US:Chase:Slate -38.45 USD Expenses:Food:Restaurant 38.45 USD 2017-01-18 * "Starbucks" "" #trip-boston-2017 Liabilities:US:Chase:Slate -5.59 USD Expenses:Food:Coffee 5.59 USD 2017-01-19 * "Giacomo's Restaurant" "" #trip-boston-2017 Liabilities:US:Chase:Slate -42.84 USD Expenses:Food:Restaurant 42.84 USD 2017-01-20 * "Giacomo's Restaurant" "" #trip-boston-2017 Liabilities:US:Chase:Slate -44.47 USD Expenses:Food:Restaurant 44.47 USD 2017-01-20 * "Franklin Cafe" "" #trip-boston-2017 Liabilities:US:Chase:Slate -29.77 USD Expenses:Food:Restaurant 29.77 USD 2017-01-20 * "Starbucks" "" #trip-boston-2017 Liabilities:US:Chase:Slate -6.43 USD Expenses:Food:Coffee 6.43 USD 2017-01-21 * "Legal Seafood" "" #trip-boston-2017 Liabilities:US:Chase:Slate -31.00 USD Expenses:Food:Restaurant 31.00 USD 2017-01-21 * "Franklin Cafe" "" #trip-boston-2017 Liabilities:US:Chase:Slate -24.75 USD Expenses:Food:Restaurant 24.75 USD 2017-01-21 * "Starbucks" "" #trip-boston-2017 Liabilities:US:Chase:Slate -5.57 USD Expenses:Food:Coffee 5.57 USD 2017-01-22 * "Legal Seafood" "" #trip-boston-2017 Liabilities:US:Chase:Slate -41.00 USD Expenses:Food:Restaurant 41.00 USD 2017-01-22 * "Franklin Cafe" "" #trip-boston-2017 Liabilities:US:Chase:Slate -40.80 USD Expenses:Food:Restaurant 40.80 USD 2017-01-22 * "Starbucks" "" #trip-boston-2017 Liabilities:US:Chase:Slate -5.51 USD Expenses:Food:Coffee 5.51 USD 2017-01-23 * "Giacomo's Restaurant" "" #trip-boston-2017 Liabilities:US:Chase:Slate -38.34 USD Expenses:Food:Restaurant 38.34 USD 2017-01-23 * "Franklin Cafe" "" #trip-boston-2017 Liabilities:US:Chase:Slate -28.50 USD Expenses:Food:Restaurant 28.50 USD 2017-01-25 * "Giacomo's Restaurant" "" #trip-boston-2017 Liabilities:US:Chase:Slate -40.68 USD Expenses:Food:Restaurant 40.68 USD 2017-01-25 * "Franklin Cafe" "" #trip-boston-2017 Liabilities:US:Chase:Slate -27.71 USD Expenses:Food:Restaurant 27.71 USD 2017-01-26 * "Giacomo's Restaurant" "" #trip-boston-2017 Liabilities:US:Chase:Slate -37.74 USD Expenses:Food:Restaurant 37.74 USD 2017-01-26 * "Franklin Cafe" "" #trip-boston-2017 Liabilities:US:Chase:Slate -35.93 USD Expenses:Food:Restaurant 35.93 USD 2017-01-26 * "Starbucks" "" #trip-boston-2017 Liabilities:US:Chase:Slate -5.41 USD Expenses:Food:Coffee 5.41 USD 2017-01-28 * "Legal Seafood" "" #trip-boston-2017 Liabilities:US:Chase:Slate -34.47 USD Expenses:Food:Restaurant 34.47 USD 2017-01-29 * "Franklin Cafe" "" #trip-boston-2017 Liabilities:US:Chase:Slate -37.70 USD Expenses:Food:Restaurant 37.70 USD 2017-01-31 * "Legal Seafood" "" #trip-boston-2017 Liabilities:US:Chase:Slate -30.45 USD Expenses:Food:Restaurant 30.45 USD 2017-01-31 * "Franklin Cafe" "" #trip-boston-2017 Liabilities:US:Chase:Slate -27.96 USD Expenses:Food:Restaurant 27.96 USD 2017-01-31 * "Starbucks" "" #trip-boston-2017 Liabilities:US:Chase:Slate -6.07 USD Expenses:Food:Coffee 6.07 USD 2017-02-01 * "Giacomo's Restaurant" "" #trip-boston-2017 Liabilities:US:Chase:Slate -41.54 USD Expenses:Food:Restaurant 41.54 USD 2017-02-01 * "Starbucks" "" #trip-boston-2017 Liabilities:US:Chase:Slate -5.18 USD Expenses:Food:Coffee 5.18 USD 2017-02-01 * "Consume vacation days" Assets:US:BayBook:Vacation -160 VACHR Expenses:Vacation 160 VACHR 2017-02-01 event "location" "New Metropolis" 2017-02-02 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -36.20 USD Expenses:Food:Restaurant 36.20 USD 2017-02-05 * "Jewel of Morroco" "Eating out with Natasha" Liabilities:US:Chase:Slate -31.48 USD Expenses:Food:Restaurant 31.48 USD 2017-02-07 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2017-02-09 balance Liabilities:US:Chase:Slate -2799.09 USD 2017-02-10 * "China Garden" "Eating out with Julie" Liabilities:US:Chase:Slate -17.15 USD Expenses:Food:Restaurant 17.15 USD 2017-02-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 387.42 USD Assets:US:BofA:Checking -387.42 USD 2017-02-12 * "Uncle Boons" "Eating out with Bill" Liabilities:US:Chase:Slate -30.25 USD Expenses:Food:Restaurant 30.25 USD 2017-02-13 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -151.58 USD Expenses:Food:Groceries 151.58 USD 2017-02-15 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -15.76 USD Expenses:Food:Restaurant 15.76 USD 2017-02-18 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -29.97 USD Expenses:Food:Restaurant 29.97 USD 2017-02-23 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -34.64 USD Expenses:Food:Restaurant 34.64 USD 2017-02-25 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -52.42 USD Expenses:Food:Groceries 52.42 USD 2017-02-27 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -17.44 USD Expenses:Food:Restaurant 17.44 USD 2017-03-02 * "Cafe Modagor" "Eating out with Julie" Liabilities:US:Chase:Slate -21.09 USD Expenses:Food:Restaurant 21.09 USD 2017-03-03 * "Jewel of Morroco" "Eating out with Natasha" Liabilities:US:Chase:Slate -14.95 USD Expenses:Food:Restaurant 14.95 USD 2017-03-04 * "Uncle Boons" "Eating out with Bill" Liabilities:US:Chase:Slate -49.09 USD Expenses:Food:Restaurant 49.09 USD 2017-03-06 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2017-03-08 balance Liabilities:US:Chase:Slate -2966.01 USD 2017-03-08 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 528.12 USD Assets:US:BofA:Checking -528.12 USD 2017-03-09 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -21.18 USD Expenses:Food:Restaurant 21.18 USD 2017-03-11 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -84.79 USD Expenses:Food:Groceries 84.79 USD 2017-03-12 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -13.62 USD Expenses:Food:Restaurant 13.62 USD 2017-03-14 * "Uncle Boons" "Eating out after work" Liabilities:US:Chase:Slate -19.41 USD Expenses:Food:Restaurant 19.41 USD 2017-03-18 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -26.87 USD Expenses:Food:Restaurant 26.87 USD 2017-03-20 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -35.30 USD Expenses:Food:Restaurant 35.30 USD 2017-03-24 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -21.57 USD Expenses:Food:Restaurant 21.57 USD 2017-03-28 balance Liabilities:US:Chase:Slate -2660.63 USD 2017-03-29 * "Kin Soy" "Eating out after work" Liabilities:US:Chase:Slate -18.48 USD Expenses:Food:Restaurant 18.48 USD 2017-03-29 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -100.36 USD Expenses:Food:Groceries 100.36 USD 2017-04-01 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -15.98 USD Expenses:Food:Restaurant 15.98 USD 2017-04-03 * "Goba Goba" "Eating out alone" Liabilities:US:Chase:Slate -19.00 USD Expenses:Food:Restaurant 19.00 USD 2017-04-07 * "Jewel of Morroco" "Eating out with Natasha" Liabilities:US:Chase:Slate -34.34 USD Expenses:Food:Restaurant 34.34 USD 2017-04-07 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -82.09 USD Expenses:Food:Groceries 82.09 USD 2017-04-08 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2017-04-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 608.74 USD Assets:US:BofA:Checking -608.74 USD 2017-04-12 * "China Garden" "Eating out " Liabilities:US:Chase:Slate -16.93 USD Expenses:Food:Restaurant 16.93 USD 2017-04-14 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -21.20 USD Expenses:Food:Restaurant 21.20 USD 2017-04-17 * "Uncle Boons" "Eating out with work buddies" Liabilities:US:Chase:Slate -57.03 USD Expenses:Food:Restaurant 57.03 USD 2017-04-18 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -88.71 USD Expenses:Food:Groceries 88.71 USD 2017-04-20 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -12.59 USD Expenses:Food:Restaurant 12.59 USD 2017-04-24 balance Liabilities:US:Chase:Slate -2638.60 USD 2017-04-24 * "Uncle Boons" "Eating out with Julie" Liabilities:US:Chase:Slate -30.04 USD Expenses:Food:Restaurant 30.04 USD 2017-04-26 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -38.30 USD Expenses:Food:Restaurant 38.30 USD 2017-04-30 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -33.68 USD Expenses:Food:Restaurant 33.68 USD 2017-05-03 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -28.87 USD Expenses:Food:Restaurant 28.87 USD 2017-05-04 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -104.59 USD Expenses:Food:Groceries 104.59 USD 2017-05-05 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2017-05-07 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -39.85 USD Expenses:Food:Restaurant 39.85 USD 2017-05-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 592.47 USD Assets:US:BofA:Checking -592.47 USD 2017-05-12 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -17.61 USD Expenses:Food:Restaurant 17.61 USD 2017-05-15 * "Jewel of Morroco" "Eating out with Julie" Liabilities:US:Chase:Slate -51.10 USD Expenses:Food:Restaurant 51.10 USD 2017-05-15 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -118.53 USD Expenses:Food:Groceries 118.53 USD 2017-05-18 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -25.04 USD Expenses:Food:Restaurant 25.04 USD 2017-05-21 balance Liabilities:US:Chase:Slate -2653.74 USD 2017-05-23 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -21.08 USD Expenses:Food:Restaurant 21.08 USD 2017-05-25 * "Kin Soy" "Eating out after work" Liabilities:US:Chase:Slate -37.72 USD Expenses:Food:Restaurant 37.72 USD 2017-05-25 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -81.06 USD Expenses:Food:Groceries 81.06 USD 2017-05-27 * "Uncle Boons" "Eating out after work" Liabilities:US:Chase:Slate -22.55 USD Expenses:Food:Restaurant 22.55 USD 2017-05-30 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -13.42 USD Expenses:Food:Restaurant 13.42 USD 2017-05-31 * "China Garden" "Eating out with Bill" Liabilities:US:Chase:Slate -19.10 USD Expenses:Food:Restaurant 19.10 USD 2017-06-03 * "Kin Soy" "Eating out after work" Liabilities:US:Chase:Slate -22.24 USD Expenses:Food:Restaurant 22.24 USD 2017-06-05 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2017-06-06 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -35.46 USD Expenses:Food:Restaurant 35.46 USD 2017-06-06 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -82.96 USD Expenses:Food:Groceries 82.96 USD 2017-06-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 688.20 USD Assets:US:BofA:Checking -688.20 USD 2017-06-09 * "China Garden" "Eating out with work buddies" Liabilities:US:Chase:Slate -37.94 USD Expenses:Food:Restaurant 37.94 USD 2017-06-10 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -36.09 USD Expenses:Food:Restaurant 36.09 USD 2017-06-11 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -58.82 USD Expenses:Food:Groceries 58.82 USD 2017-06-14 * "Rose Flower" "Eating out " Liabilities:US:Chase:Slate -24.58 USD Expenses:Food:Restaurant 24.58 USD 2017-06-15 balance Liabilities:US:Chase:Slate -2578.56 USD 2017-06-17 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -35.63 USD Expenses:Food:Restaurant 35.63 USD 2017-06-20 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -23.74 USD Expenses:Food:Restaurant 23.74 USD 2017-06-21 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -23.92 USD Expenses:Food:Restaurant 23.92 USD 2017-06-21 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -76.81 USD Expenses:Food:Groceries 76.81 USD 2017-06-23 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -36.45 USD Expenses:Food:Restaurant 36.45 USD 2017-06-27 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -46.98 USD Expenses:Food:Restaurant 46.98 USD 2017-06-29 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -98.28 USD Expenses:Food:Groceries 98.28 USD 2017-06-30 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -13.77 USD Expenses:Food:Restaurant 13.77 USD 2017-07-02 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2017-07-05 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -27.86 USD Expenses:Food:Restaurant 27.86 USD 2017-07-08 balance Liabilities:US:Chase:Slate -3082.00 USD 2017-07-10 * "Kin Soy" "Eating out after work" Liabilities:US:Chase:Slate -26.73 USD Expenses:Food:Restaurant 26.73 USD 2017-07-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 649.66 USD Assets:US:BofA:Checking -649.66 USD 2017-07-11 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -36.94 USD Expenses:Food:Restaurant 36.94 USD 2017-07-14 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -34.22 USD Expenses:Food:Restaurant 34.22 USD 2017-07-16 event "location" "New York" 2017-07-17 * "Uncle Boons" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -41.41 USD Expenses:Food:Restaurant 41.41 USD 2017-07-17 * "La Colombe" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -4.60 USD Expenses:Food:Coffee 4.60 USD 2017-07-18 * "La Colombe" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -6.57 USD Expenses:Food:Coffee 6.57 USD 2017-07-18 * "Gimme! Coffee" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -7.55 USD Expenses:Food:Coffee 7.55 USD 2017-07-19 * "Cafe Select" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -31.97 USD Expenses:Food:Restaurant 31.97 USD 2017-07-19 * "La Colombe" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -5.94 USD Expenses:Food:Coffee 5.94 USD 2017-07-20 * "Cafe Select" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -29.50 USD Expenses:Food:Restaurant 29.50 USD 2017-07-21 * "Uncle Boons" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -47.38 USD Expenses:Food:Restaurant 47.38 USD 2017-07-21 * "Cafe Select" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -34.95 USD Expenses:Food:Restaurant 34.95 USD 2017-07-22 * "La Colombe" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -5.78 USD Expenses:Food:Coffee 5.78 USD 2017-07-23 * "Uncle Boons" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -37.09 USD Expenses:Food:Restaurant 37.09 USD 2017-07-23 * "Cafe Select" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -23.34 USD Expenses:Food:Restaurant 23.34 USD 2017-07-23 * "Laut" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -31.21 USD Expenses:Food:Restaurant 31.21 USD 2017-07-24 * "Takahachi" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -52.81 USD Expenses:Food:Restaurant 52.81 USD 2017-07-24 * "Laut" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -34.24 USD Expenses:Food:Restaurant 34.24 USD 2017-07-24 * "La Colombe" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -5.18 USD Expenses:Food:Coffee 5.18 USD 2017-07-24 * "Gimme! Coffee" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -6.60 USD Expenses:Food:Coffee 6.60 USD 2017-07-25 * "Cafe Select" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -31.28 USD Expenses:Food:Restaurant 31.28 USD 2017-07-25 * "Gimme! Coffee" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -6.78 USD Expenses:Food:Coffee 6.78 USD 2017-07-26 * "Takahachi" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -49.02 USD Expenses:Food:Restaurant 49.02 USD 2017-07-26 * "Laut" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -29.18 USD Expenses:Food:Restaurant 29.18 USD 2017-07-27 * "Uncle Boons" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -36.76 USD Expenses:Food:Restaurant 36.76 USD 2017-07-27 * "Takahachi" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -43.97 USD Expenses:Food:Restaurant 43.97 USD 2017-07-28 * "Uncle Boons" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -38.44 USD Expenses:Food:Restaurant 38.44 USD 2017-07-28 * "Cafe Select" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -34.99 USD Expenses:Food:Restaurant 34.99 USD 2017-07-28 * "Takahachi" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -49.73 USD Expenses:Food:Restaurant 49.73 USD 2017-07-29 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -43.83 USD Expenses:Food:Restaurant 43.83 USD 2017-07-29 * "Laut" "" #trip-new-york-2017 Liabilities:US:Chase:Slate -38.75 USD Expenses:Food:Restaurant 38.75 USD 2017-07-29 * "Consume vacation days" Assets:US:BayBook:Vacation -104 VACHR Expenses:Vacation 104 VACHR 2017-07-29 event "location" "New Metropolis" 2017-08-02 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2017-08-03 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -14.43 USD Expenses:Food:Restaurant 14.43 USD 2017-08-07 balance Liabilities:US:Chase:Slate -3473.51 USD 2017-08-08 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -20.33 USD Expenses:Food:Restaurant 20.33 USD 2017-08-08 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 269.75 USD Assets:US:BofA:Checking -269.75 USD 2017-08-09 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -17.14 USD Expenses:Food:Restaurant 17.14 USD 2017-08-14 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -40.16 USD Expenses:Food:Restaurant 40.16 USD 2017-08-15 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -60.91 USD Expenses:Food:Groceries 60.91 USD 2017-08-16 * "Chichipotle" "Eating out after work" Liabilities:US:Chase:Slate -19.49 USD Expenses:Food:Restaurant 19.49 USD 2017-08-17 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -25.12 USD Expenses:Food:Restaurant 25.12 USD 2017-08-20 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -28.82 USD Expenses:Food:Restaurant 28.82 USD 2017-08-25 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -17.11 USD Expenses:Food:Restaurant 17.11 USD 2017-08-29 * "China Garden" "Eating out with work buddies" Liabilities:US:Chase:Slate -17.56 USD Expenses:Food:Restaurant 17.56 USD 2017-09-01 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -112.86 USD Expenses:Food:Groceries 112.86 USD 2017-09-03 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -47.59 USD Expenses:Food:Restaurant 47.59 USD 2017-09-04 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -28.66 USD Expenses:Food:Restaurant 28.66 USD 2017-09-04 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2017-09-06 balance Liabilities:US:Chase:Slate -3759.51 USD 2017-09-06 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -47.78 USD Expenses:Food:Restaurant 47.78 USD 2017-09-08 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -14.41 USD Expenses:Food:Restaurant 14.41 USD * Taxable Investments 2015-01-01 open Assets:US:ETrade:Cash USD 2015-01-01 open Assets:US:ETrade:ITOT ITOT 2015-01-01 open Assets:US:ETrade:VEA VEA 2015-01-01 open Assets:US:ETrade:VHT VHT 2015-01-01 open Assets:US:ETrade:GLD GLD 2015-01-01 open Income:US:ETrade:Gains USD 2015-01-01 open Income:US:ETrade:Dividends USD 2015-09-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 0.00 USD Income:US:ETrade:Dividends 0.00 USD 2015-09-20 * "Buy shares of VHT" Assets:US:ETrade:Cash -4341.10 USD Assets:US:ETrade:VHT 45 VHT {96.27 USD, 2015-09-20} Expenses:Financial:Commissions 8.95 USD 2015-11-06 * "Sell shares of VHT" Assets:US:ETrade:VHT -45 VHT {96.27 USD, 2015-09-20} @ 93.77 USD Assets:US:ETrade:Cash 4210.70 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains 112.50 USD 2015-12-08 * "Buy shares of GLD" Assets:US:ETrade:Cash -8297.45 USD Assets:US:ETrade:GLD 137 GLD {60.50 USD, 2015-12-08} Expenses:Financial:Commissions 8.95 USD 2015-12-17 * "Dividends on portfolio" Assets:US:ETrade:Cash 50.48 USD Income:US:ETrade:Dividends -50.48 USD 2016-01-05 * "Sell shares of GLD" Assets:US:ETrade:GLD -137 GLD {60.50 USD, 2015-12-08} @ 58.30 USD Assets:US:ETrade:Cash 7978.15 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains 301.40 USD 2016-01-31 * "Buy shares of GLD" Assets:US:ETrade:Cash -3965.19 USD Assets:US:ETrade:GLD 68 GLD {58.18 USD, 2016-01-31} Expenses:Financial:Commissions 8.95 USD 2016-01-31 * "Buy shares of ITOT" Assets:US:ETrade:Cash -3851.47 USD Assets:US:ETrade:ITOT 22 ITOT {174.66 USD, 2016-01-31} Expenses:Financial:Commissions 8.95 USD 2016-02-11 * "Sell shares of ITOT" Assets:US:ETrade:ITOT -22 ITOT {174.66 USD, 2016-01-31} @ 174.24 USD Assets:US:ETrade:Cash 3824.33 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains 9.24 USD 2016-02-15 * "Buy shares of VHT" Assets:US:ETrade:Cash -3995.38 USD Assets:US:ETrade:VHT 41 VHT {97.23 USD, 2016-02-15} Expenses:Financial:Commissions 8.95 USD 2016-03-17 * "Dividends on portfolio" Assets:US:ETrade:Cash 97.62 USD Income:US:ETrade:Dividends -97.62 USD 2016-06-17 * "Dividends on portfolio" Assets:US:ETrade:Cash 97.62 USD Income:US:ETrade:Dividends -97.62 USD 2016-06-28 * "Sell shares of VHT" Assets:US:ETrade:VHT -41 VHT {97.23 USD, 2016-02-15} @ 97.13 USD Assets:US:ETrade:Cash 3973.38 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains 4.10 USD 2016-07-04 * "Buy shares of VHT" Assets:US:ETrade:Cash -970.65 USD Assets:US:ETrade:VHT 10 VHT {96.17 USD, 2016-07-04} Expenses:Financial:Commissions 8.95 USD 2016-07-04 * "Buy shares of GLD" Assets:US:ETrade:Cash -987.35 USD Assets:US:ETrade:GLD 16 GLD {61.15 USD, 2016-07-04} Expenses:Financial:Commissions 8.95 USD 2016-07-04 * "Buy shares of VEA" Assets:US:ETrade:Cash -916.75 USD Assets:US:ETrade:VEA 5 VEA {181.56 USD, 2016-07-04} Expenses:Financial:Commissions 8.95 USD 2016-07-04 * "Buy shares of ITOT" Assets:US:ETrade:Cash -884.85 USD Assets:US:ETrade:ITOT 5 ITOT {175.18 USD, 2016-07-04} Expenses:Financial:Commissions 8.95 USD 2016-07-16 * "Sell shares of VHT" Assets:US:ETrade:VHT -10 VHT {96.17 USD, 2016-07-04} @ 93.67 USD Assets:US:ETrade:Cash 927.75 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains 25.00 USD 2016-07-25 * "Buy shares of GLD" Assets:US:ETrade:Cash -499.59 USD Assets:US:ETrade:GLD 8 GLD {61.33 USD, 2016-07-25} Expenses:Financial:Commissions 8.95 USD 2016-07-25 * "Buy shares of VEA" Assets:US:ETrade:Cash -381.17 USD Assets:US:ETrade:VEA 2 VEA {186.11 USD, 2016-07-25} Expenses:Financial:Commissions 8.95 USD 2016-08-19 * "Buy shares of ITOT" Assets:US:ETrade:Cash -1254.39 USD Assets:US:ETrade:ITOT 7 ITOT {177.92 USD, 2016-08-19} Expenses:Financial:Commissions 8.95 USD 2016-08-19 * "Buy shares of VHT" Assets:US:ETrade:Cash -1391.73 USD Assets:US:ETrade:VHT 14 VHT {98.77 USD, 2016-08-19} Expenses:Financial:Commissions 8.95 USD 2016-08-19 * "Buy shares of VEA" Assets:US:ETrade:Cash -1315.22 USD Assets:US:ETrade:VEA 7 VEA {186.61 USD, 2016-08-19} Expenses:Financial:Commissions 8.95 USD 2016-09-17 * "Dividends on portfolio" Assets:US:ETrade:Cash 131.71 USD Income:US:ETrade:Dividends -131.71 USD 2016-11-17 * "Buy shares of GLD" Assets:US:ETrade:Cash -1405.29 USD Assets:US:ETrade:GLD 22 GLD {63.47 USD, 2016-11-17} Expenses:Financial:Commissions 8.95 USD 2016-11-17 * "Buy shares of VEA" Assets:US:ETrade:Cash -1372.06 USD Assets:US:ETrade:VEA 7 VEA {194.73 USD, 2016-11-17} Expenses:Financial:Commissions 8.95 USD 2016-11-17 * "Buy shares of VHT" Assets:US:ETrade:Cash -1334.43 USD Assets:US:ETrade:VHT 13 VHT {101.96 USD, 2016-11-17} Expenses:Financial:Commissions 8.95 USD 2016-11-26 * "Buy shares of VEA" Assets:US:ETrade:Cash -3076.95 USD Assets:US:ETrade:VEA 16 VEA {191.75 USD, 2016-11-26} Expenses:Financial:Commissions 8.95 USD 2016-12-17 * "Dividends on portfolio" Assets:US:ETrade:Cash 160.32 USD Income:US:ETrade:Dividends -160.32 USD 2017-03-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 160.32 USD Income:US:ETrade:Dividends -160.32 USD 2017-04-01 * "Sell shares of GLD" Assets:US:ETrade:GLD -68 GLD {58.18 USD, 2016-01-31} @ 72.35 USD Assets:US:ETrade:Cash 4910.85 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -963.56 USD 2017-04-07 * "Sell shares of VEA" Assets:US:ETrade:VEA -16 VEA {191.75 USD, 2016-11-26} @ 210.70 USD Assets:US:ETrade:Cash 3362.25 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -303.20 USD 2017-04-11 * "Buy shares of GLD" Assets:US:ETrade:Cash -2801.35 USD Assets:US:ETrade:GLD 39 GLD {71.60 USD, 2017-04-11} Expenses:Financial:Commissions 8.95 USD 2017-04-11 * "Buy shares of ITOT" Assets:US:ETrade:Cash -2740.90 USD Assets:US:ETrade:ITOT 15 ITOT {182.13 USD, 2017-04-11} Expenses:Financial:Commissions 8.95 USD 2017-04-11 * "Buy shares of VEA" Assets:US:ETrade:Cash -2748.05 USD Assets:US:ETrade:VEA 13 VEA {210.70 USD, 2017-04-11} Expenses:Financial:Commissions 8.95 USD 2017-06-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 193.37 USD Income:US:ETrade:Dividends -193.37 USD 2017-09-03 * "Sell shares of VEA" Assets:US:ETrade:VEA -7 VEA {186.61 USD, 2016-08-19} @ 231.26 USD Assets:US:ETrade:Cash 1609.87 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -312.55 USD * Vanguard Investments 2015-01-01 open Assets:US:Vanguard:VBMPX VBMPX number: "882882" 2015-01-01 open Assets:US:Vanguard:RGAGX RGAGX number: "882882" 2015-01-01 open Assets:US:Vanguard USD address: "P.O. Box 1110, Valley Forge, PA 19482-1110" institution: "Vanguard Group" phone: "+1.800.523.1188" 2015-01-01 open Income:US:BayBook:Match401k USD 2015-01-01 open Assets:US:Vanguard:Cash USD number: "882882" 2015-01-02 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2015-01-05 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 9.752 VBMPX {49.22 USD, 2015-01-05} Assets:US:Vanguard:Cash -479.99 USD 2015-01-05 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 11.055 RGAGX {65.13 USD, 2015-01-05} Assets:US:Vanguard:Cash -720.01 USD 2015-01-05 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.876 VBMPX {49.22 USD, 2015-01-05} Assets:US:Vanguard:Cash -240.00 USD 2015-01-05 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.527 RGAGX {65.13 USD, 2015-01-05} Assets:US:Vanguard:Cash -359.97 USD 2015-01-16 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2015-01-19 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 9.689 VBMPX {49.54 USD, 2015-01-19} Assets:US:Vanguard:Cash -479.99 USD 2015-01-19 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 11.142 RGAGX {64.62 USD, 2015-01-19} Assets:US:Vanguard:Cash -720.00 USD 2015-01-19 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.845 VBMPX {49.54 USD, 2015-01-19} Assets:US:Vanguard:Cash -240.02 USD 2015-01-19 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.571 RGAGX {64.62 USD, 2015-01-19} Assets:US:Vanguard:Cash -360.00 USD 2015-01-30 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2015-02-02 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 9.645 VBMPX {49.77 USD, 2015-02-02} Assets:US:Vanguard:Cash -480.03 USD 2015-02-02 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 11.376 RGAGX {63.29 USD, 2015-02-02} Assets:US:Vanguard:Cash -719.99 USD 2015-02-02 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.822 VBMPX {49.77 USD, 2015-02-02} Assets:US:Vanguard:Cash -239.99 USD 2015-02-02 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.688 RGAGX {63.29 USD, 2015-02-02} Assets:US:Vanguard:Cash -359.99 USD 2015-02-13 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2015-02-16 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 9.639 VBMPX {49.80 USD, 2015-02-16} Assets:US:Vanguard:Cash -480.02 USD 2015-02-16 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.845 RGAGX {66.39 USD, 2015-02-16} Assets:US:Vanguard:Cash -720.00 USD 2015-02-16 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.819 VBMPX {49.80 USD, 2015-02-16} Assets:US:Vanguard:Cash -239.99 USD 2015-02-16 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.423 RGAGX {66.39 USD, 2015-02-16} Assets:US:Vanguard:Cash -360.03 USD 2015-02-27 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2015-03-02 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 9.421 VBMPX {50.95 USD, 2015-03-02} Assets:US:Vanguard:Cash -480.00 USD 2015-03-02 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 11.019 RGAGX {65.34 USD, 2015-03-02} Assets:US:Vanguard:Cash -719.98 USD 2015-03-02 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.711 VBMPX {50.95 USD, 2015-03-02} Assets:US:Vanguard:Cash -240.03 USD 2015-03-02 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.510 RGAGX {65.34 USD, 2015-03-02} Assets:US:Vanguard:Cash -360.02 USD 2015-03-13 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2015-03-16 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 9.391 VBMPX {51.11 USD, 2015-03-16} Assets:US:Vanguard:Cash -479.97 USD 2015-03-16 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.655 RGAGX {67.57 USD, 2015-03-16} Assets:US:Vanguard:Cash -719.96 USD 2015-03-16 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.696 VBMPX {51.11 USD, 2015-03-16} Assets:US:Vanguard:Cash -240.01 USD 2015-03-16 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.328 RGAGX {67.57 USD, 2015-03-16} Assets:US:Vanguard:Cash -360.01 USD 2015-03-27 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2015-03-30 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 9.157 VBMPX {52.42 USD, 2015-03-30} Assets:US:Vanguard:Cash -480.01 USD 2015-03-30 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.855 RGAGX {66.33 USD, 2015-03-30} Assets:US:Vanguard:Cash -720.01 USD 2015-03-30 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.578 VBMPX {52.42 USD, 2015-03-30} Assets:US:Vanguard:Cash -239.98 USD 2015-03-30 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.427 RGAGX {66.33 USD, 2015-03-30} Assets:US:Vanguard:Cash -359.97 USD 2015-04-10 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2015-04-13 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 9.143 VBMPX {52.50 USD, 2015-04-13} Assets:US:Vanguard:Cash -480.01 USD 2015-04-13 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.550 RGAGX {68.25 USD, 2015-04-13} Assets:US:Vanguard:Cash -720.04 USD 2015-04-13 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.571 VBMPX {52.50 USD, 2015-04-13} Assets:US:Vanguard:Cash -239.98 USD 2015-04-13 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.275 RGAGX {68.25 USD, 2015-04-13} Assets:US:Vanguard:Cash -360.02 USD 2015-04-24 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2015-04-27 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 9.070 VBMPX {52.92 USD, 2015-04-27} Assets:US:Vanguard:Cash -479.98 USD 2015-04-27 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.860 RGAGX {66.30 USD, 2015-04-27} Assets:US:Vanguard:Cash -720.02 USD 2015-04-27 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.535 VBMPX {52.92 USD, 2015-04-27} Assets:US:Vanguard:Cash -239.99 USD 2015-04-27 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.430 RGAGX {66.30 USD, 2015-04-27} Assets:US:Vanguard:Cash -360.01 USD 2015-05-08 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2015-05-11 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 9.034 VBMPX {53.13 USD, 2015-05-11} Assets:US:Vanguard:Cash -479.98 USD 2015-05-11 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 11.024 RGAGX {65.31 USD, 2015-05-11} Assets:US:Vanguard:Cash -719.98 USD 2015-05-11 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.517 VBMPX {53.13 USD, 2015-05-11} Assets:US:Vanguard:Cash -239.99 USD 2015-05-11 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.512 RGAGX {65.31 USD, 2015-05-11} Assets:US:Vanguard:Cash -359.99 USD 2015-05-22 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2015-05-25 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 9.047 VBMPX {53.06 USD, 2015-05-25} Assets:US:Vanguard:Cash -480.03 USD 2015-05-25 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 11.491 RGAGX {62.66 USD, 2015-05-25} Assets:US:Vanguard:Cash -720.03 USD 2015-05-25 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.523 VBMPX {53.06 USD, 2015-05-25} Assets:US:Vanguard:Cash -239.99 USD 2015-05-25 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.745 RGAGX {62.66 USD, 2015-05-25} Assets:US:Vanguard:Cash -359.98 USD 2015-06-05 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2015-06-08 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 8.967 VBMPX {53.53 USD, 2015-06-08} Assets:US:Vanguard:Cash -480.00 USD 2015-06-08 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.839 RGAGX {66.43 USD, 2015-06-08} Assets:US:Vanguard:Cash -720.03 USD 2015-06-08 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.483 VBMPX {53.53 USD, 2015-06-08} Assets:US:Vanguard:Cash -239.97 USD 2015-06-08 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.419 RGAGX {66.43 USD, 2015-06-08} Assets:US:Vanguard:Cash -359.98 USD 2015-06-19 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2015-06-22 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 9.028 VBMPX {53.17 USD, 2015-06-22} Assets:US:Vanguard:Cash -480.02 USD 2015-06-22 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 11.035 RGAGX {65.25 USD, 2015-06-22} Assets:US:Vanguard:Cash -720.03 USD 2015-06-22 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.514 VBMPX {53.17 USD, 2015-06-22} Assets:US:Vanguard:Cash -240.01 USD 2015-06-22 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.517 RGAGX {65.25 USD, 2015-06-22} Assets:US:Vanguard:Cash -359.98 USD 2015-07-03 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2015-07-06 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 9.006 VBMPX {53.30 USD, 2015-07-06} Assets:US:Vanguard:Cash -480.02 USD 2015-07-06 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.959 RGAGX {65.70 USD, 2015-07-06} Assets:US:Vanguard:Cash -720.01 USD 2015-07-06 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.503 VBMPX {53.30 USD, 2015-07-06} Assets:US:Vanguard:Cash -240.01 USD 2015-07-06 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.479 RGAGX {65.70 USD, 2015-07-06} Assets:US:Vanguard:Cash -359.97 USD 2015-07-17 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2015-07-20 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 8.912 VBMPX {53.86 USD, 2015-07-20} Assets:US:Vanguard:Cash -480.00 USD 2015-07-20 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.939 RGAGX {65.82 USD, 2015-07-20} Assets:US:Vanguard:Cash -720.00 USD 2015-07-20 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.456 VBMPX {53.86 USD, 2015-07-20} Assets:US:Vanguard:Cash -240.00 USD 2015-07-20 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.469 RGAGX {65.82 USD, 2015-07-20} Assets:US:Vanguard:Cash -359.97 USD 2016-01-15 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2016-01-18 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 8.140 VBMPX {58.97 USD, 2016-01-18} Assets:US:Vanguard:Cash -480.02 USD 2016-01-18 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.520 RGAGX {68.44 USD, 2016-01-18} Assets:US:Vanguard:Cash -719.99 USD 2016-01-18 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.070 VBMPX {58.97 USD, 2016-01-18} Assets:US:Vanguard:Cash -240.01 USD 2016-01-18 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.260 RGAGX {68.44 USD, 2016-01-18} Assets:US:Vanguard:Cash -359.99 USD 2016-01-29 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2016-02-01 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 8.099 VBMPX {59.27 USD, 2016-02-01} Assets:US:Vanguard:Cash -480.03 USD 2016-02-01 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.085 RGAGX {71.39 USD, 2016-02-01} Assets:US:Vanguard:Cash -719.97 USD 2016-02-01 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.049 VBMPX {59.27 USD, 2016-02-01} Assets:US:Vanguard:Cash -239.98 USD 2016-02-01 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.043 RGAGX {71.39 USD, 2016-02-01} Assets:US:Vanguard:Cash -360.02 USD 2016-02-12 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2016-02-15 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 8.163 VBMPX {58.80 USD, 2016-02-15} Assets:US:Vanguard:Cash -479.98 USD 2016-02-15 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.061 RGAGX {71.56 USD, 2016-02-15} Assets:US:Vanguard:Cash -719.97 USD 2016-02-15 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.082 VBMPX {58.80 USD, 2016-02-15} Assets:US:Vanguard:Cash -240.02 USD 2016-02-15 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.031 RGAGX {71.56 USD, 2016-02-15} Assets:US:Vanguard:Cash -360.02 USD 2016-02-26 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2016-02-29 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 8.235 VBMPX {58.29 USD, 2016-02-29} Assets:US:Vanguard:Cash -480.02 USD 2016-02-29 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 9.992 RGAGX {72.06 USD, 2016-02-29} Assets:US:Vanguard:Cash -720.02 USD 2016-02-29 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.117 VBMPX {58.29 USD, 2016-02-29} Assets:US:Vanguard:Cash -239.98 USD 2016-02-29 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.996 RGAGX {72.06 USD, 2016-02-29} Assets:US:Vanguard:Cash -360.01 USD 2016-03-11 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2016-03-14 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 8.183 VBMPX {58.66 USD, 2016-03-14} Assets:US:Vanguard:Cash -480.01 USD 2016-03-14 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 9.509 RGAGX {75.72 USD, 2016-03-14} Assets:US:Vanguard:Cash -720.02 USD 2016-03-14 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.091 VBMPX {58.66 USD, 2016-03-14} Assets:US:Vanguard:Cash -239.98 USD 2016-03-14 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.754 RGAGX {75.72 USD, 2016-03-14} Assets:US:Vanguard:Cash -359.97 USD 2016-03-25 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2016-03-28 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 8.219 VBMPX {58.40 USD, 2016-03-28} Assets:US:Vanguard:Cash -479.99 USD 2016-03-28 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 9.453 RGAGX {76.17 USD, 2016-03-28} Assets:US:Vanguard:Cash -720.04 USD 2016-03-28 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.109 VBMPX {58.40 USD, 2016-03-28} Assets:US:Vanguard:Cash -239.97 USD 2016-03-28 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.726 RGAGX {76.17 USD, 2016-03-28} Assets:US:Vanguard:Cash -359.98 USD 2016-04-08 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2016-04-11 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 8.193 VBMPX {58.59 USD, 2016-04-11} Assets:US:Vanguard:Cash -480.03 USD 2016-04-11 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 9.295 RGAGX {77.46 USD, 2016-04-11} Assets:US:Vanguard:Cash -719.99 USD 2016-04-11 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.096 VBMPX {58.59 USD, 2016-04-11} Assets:US:Vanguard:Cash -239.98 USD 2016-04-11 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.648 RGAGX {77.46 USD, 2016-04-11} Assets:US:Vanguard:Cash -360.03 USD 2016-04-22 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2016-04-25 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 8.090 VBMPX {59.33 USD, 2016-04-25} Assets:US:Vanguard:Cash -479.98 USD 2016-04-25 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 9.487 RGAGX {75.89 USD, 2016-04-25} Assets:US:Vanguard:Cash -719.97 USD 2016-04-25 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.045 VBMPX {59.33 USD, 2016-04-25} Assets:US:Vanguard:Cash -239.99 USD 2016-04-25 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.744 RGAGX {75.89 USD, 2016-04-25} Assets:US:Vanguard:Cash -360.02 USD 2016-05-06 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2016-05-09 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 8.082 VBMPX {59.39 USD, 2016-05-09} Assets:US:Vanguard:Cash -479.99 USD 2016-05-09 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 9.568 RGAGX {75.25 USD, 2016-05-09} Assets:US:Vanguard:Cash -719.99 USD 2016-05-09 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.041 VBMPX {59.39 USD, 2016-05-09} Assets:US:Vanguard:Cash -239.99 USD 2016-05-09 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.784 RGAGX {75.25 USD, 2016-05-09} Assets:US:Vanguard:Cash -360.00 USD 2016-05-20 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2016-05-23 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 8.008 VBMPX {59.94 USD, 2016-05-23} Assets:US:Vanguard:Cash -480.00 USD 2016-05-23 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 9.978 RGAGX {72.16 USD, 2016-05-23} Assets:US:Vanguard:Cash -720.01 USD 2016-05-23 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.004 VBMPX {59.94 USD, 2016-05-23} Assets:US:Vanguard:Cash -240.00 USD 2016-05-23 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.989 RGAGX {72.16 USD, 2016-05-23} Assets:US:Vanguard:Cash -360.01 USD 2016-06-03 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2016-06-06 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 7.941 VBMPX {60.45 USD, 2016-06-06} Assets:US:Vanguard:Cash -480.03 USD 2016-06-06 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.187 RGAGX {70.68 USD, 2016-06-06} Assets:US:Vanguard:Cash -720.02 USD 2016-06-06 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.970 VBMPX {60.45 USD, 2016-06-06} Assets:US:Vanguard:Cash -239.99 USD 2016-06-06 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.093 RGAGX {70.68 USD, 2016-06-06} Assets:US:Vanguard:Cash -359.97 USD 2016-06-17 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2016-06-20 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 7.978 VBMPX {60.17 USD, 2016-06-20} Assets:US:Vanguard:Cash -480.04 USD 2016-06-20 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.941 RGAGX {65.81 USD, 2016-06-20} Assets:US:Vanguard:Cash -720.03 USD 2016-06-20 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.988 VBMPX {60.17 USD, 2016-06-20} Assets:US:Vanguard:Cash -239.96 USD 2016-06-20 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.470 RGAGX {65.81 USD, 2016-06-20} Assets:US:Vanguard:Cash -359.98 USD 2016-07-01 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2016-07-04 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 7.904 VBMPX {60.73 USD, 2016-07-04} Assets:US:Vanguard:Cash -480.01 USD 2016-07-04 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 11.118 RGAGX {64.76 USD, 2016-07-04} Assets:US:Vanguard:Cash -720.00 USD 2016-07-04 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.952 VBMPX {60.73 USD, 2016-07-04} Assets:US:Vanguard:Cash -240.00 USD 2016-07-04 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.559 RGAGX {64.76 USD, 2016-07-04} Assets:US:Vanguard:Cash -360.00 USD 2016-07-15 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2016-07-18 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 7.875 VBMPX {60.95 USD, 2016-07-18} Assets:US:Vanguard:Cash -479.98 USD 2016-07-18 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.629 RGAGX {67.74 USD, 2016-07-18} Assets:US:Vanguard:Cash -720.01 USD 2016-07-18 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.938 VBMPX {60.95 USD, 2016-07-18} Assets:US:Vanguard:Cash -240.02 USD 2016-07-18 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.315 RGAGX {67.74 USD, 2016-07-18} Assets:US:Vanguard:Cash -360.04 USD 2016-07-29 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2016-08-01 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 7.911 VBMPX {60.67 USD, 2016-08-01} Assets:US:Vanguard:Cash -479.96 USD 2016-08-01 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.569 RGAGX {68.12 USD, 2016-08-01} Assets:US:Vanguard:Cash -719.96 USD 2016-08-01 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.956 VBMPX {60.67 USD, 2016-08-01} Assets:US:Vanguard:Cash -240.01 USD 2016-08-01 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.285 RGAGX {68.12 USD, 2016-08-01} Assets:US:Vanguard:Cash -360.01 USD 2017-01-13 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2017-01-16 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 7.838 VBMPX {61.24 USD, 2017-01-16} Assets:US:Vanguard:Cash -480.00 USD 2017-01-16 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.620 RGAGX {67.80 USD, 2017-01-16} Assets:US:Vanguard:Cash -720.04 USD 2017-01-16 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.919 VBMPX {61.24 USD, 2017-01-16} Assets:US:Vanguard:Cash -240.00 USD 2017-01-16 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.310 RGAGX {67.80 USD, 2017-01-16} Assets:US:Vanguard:Cash -360.02 USD 2017-01-27 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2017-01-30 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 7.826 VBMPX {61.33 USD, 2017-01-30} Assets:US:Vanguard:Cash -479.97 USD 2017-01-30 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.403 RGAGX {69.21 USD, 2017-01-30} Assets:US:Vanguard:Cash -719.99 USD 2017-01-30 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.913 VBMPX {61.33 USD, 2017-01-30} Assets:US:Vanguard:Cash -239.98 USD 2017-01-30 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.202 RGAGX {69.21 USD, 2017-01-30} Assets:US:Vanguard:Cash -360.03 USD 2017-02-10 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2017-02-13 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 7.867 VBMPX {61.01 USD, 2017-02-13} Assets:US:Vanguard:Cash -479.97 USD 2017-02-13 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.741 RGAGX {67.03 USD, 2017-02-13} Assets:US:Vanguard:Cash -719.97 USD 2017-02-13 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.934 VBMPX {61.01 USD, 2017-02-13} Assets:US:Vanguard:Cash -240.01 USD 2017-02-13 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.371 RGAGX {67.03 USD, 2017-02-13} Assets:US:Vanguard:Cash -360.02 USD 2017-02-24 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2017-02-27 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 7.707 VBMPX {62.28 USD, 2017-02-27} Assets:US:Vanguard:Cash -479.99 USD 2017-02-27 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.405 RGAGX {69.20 USD, 2017-02-27} Assets:US:Vanguard:Cash -720.03 USD 2017-02-27 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.854 VBMPX {62.28 USD, 2017-02-27} Assets:US:Vanguard:Cash -240.03 USD 2017-02-27 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.202 RGAGX {69.20 USD, 2017-02-27} Assets:US:Vanguard:Cash -359.98 USD 2017-03-10 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2017-03-13 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 7.677 VBMPX {62.52 USD, 2017-03-13} Assets:US:Vanguard:Cash -479.97 USD 2017-03-13 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.182 RGAGX {70.71 USD, 2017-03-13} Assets:US:Vanguard:Cash -719.97 USD 2017-03-13 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.839 VBMPX {62.52 USD, 2017-03-13} Assets:US:Vanguard:Cash -240.01 USD 2017-03-13 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.092 RGAGX {70.71 USD, 2017-03-13} Assets:US:Vanguard:Cash -360.06 USD 2017-03-24 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2017-03-27 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 7.663 VBMPX {62.64 USD, 2017-03-27} Assets:US:Vanguard:Cash -480.01 USD 2017-03-27 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 9.748 RGAGX {73.86 USD, 2017-03-27} Assets:US:Vanguard:Cash -719.99 USD 2017-03-27 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.831 VBMPX {62.64 USD, 2017-03-27} Assets:US:Vanguard:Cash -239.97 USD 2017-03-27 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.874 RGAGX {73.86 USD, 2017-03-27} Assets:US:Vanguard:Cash -359.99 USD 2017-04-07 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2017-04-10 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 7.792 VBMPX {61.60 USD, 2017-04-10} Assets:US:Vanguard:Cash -479.99 USD 2017-04-10 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.057 RGAGX {71.59 USD, 2017-04-10} Assets:US:Vanguard:Cash -719.98 USD 2017-04-10 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.896 VBMPX {61.60 USD, 2017-04-10} Assets:US:Vanguard:Cash -239.99 USD 2017-04-10 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.029 RGAGX {71.59 USD, 2017-04-10} Assets:US:Vanguard:Cash -360.03 USD 2017-04-21 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2017-04-24 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 7.718 VBMPX {62.19 USD, 2017-04-24} Assets:US:Vanguard:Cash -479.98 USD 2017-04-24 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.467 RGAGX {68.79 USD, 2017-04-24} Assets:US:Vanguard:Cash -720.02 USD 2017-04-24 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.859 VBMPX {62.19 USD, 2017-04-24} Assets:US:Vanguard:Cash -239.99 USD 2017-04-24 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.234 RGAGX {68.79 USD, 2017-04-24} Assets:US:Vanguard:Cash -360.05 USD 2017-05-05 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2017-05-08 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 7.671 VBMPX {62.57 USD, 2017-05-08} Assets:US:Vanguard:Cash -479.97 USD 2017-05-08 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.337 RGAGX {69.65 USD, 2017-05-08} Assets:US:Vanguard:Cash -719.97 USD 2017-05-08 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.836 VBMPX {62.57 USD, 2017-05-08} Assets:US:Vanguard:Cash -240.02 USD 2017-05-08 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.169 RGAGX {69.65 USD, 2017-05-08} Assets:US:Vanguard:Cash -360.02 USD 2017-05-19 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2017-05-22 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 7.654 VBMPX {62.71 USD, 2017-05-22} Assets:US:Vanguard:Cash -479.98 USD 2017-05-22 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.217 RGAGX {70.47 USD, 2017-05-22} Assets:US:Vanguard:Cash -719.99 USD 2017-05-22 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.827 VBMPX {62.71 USD, 2017-05-22} Assets:US:Vanguard:Cash -239.99 USD 2017-05-22 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.109 RGAGX {70.47 USD, 2017-05-22} Assets:US:Vanguard:Cash -360.03 USD 2017-06-02 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2017-06-05 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 7.676 VBMPX {62.53 USD, 2017-06-05} Assets:US:Vanguard:Cash -479.98 USD 2017-06-05 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.148 RGAGX {70.95 USD, 2017-06-05} Assets:US:Vanguard:Cash -720.00 USD 2017-06-05 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.838 VBMPX {62.53 USD, 2017-06-05} Assets:US:Vanguard:Cash -239.99 USD 2017-06-05 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.074 RGAGX {70.95 USD, 2017-06-05} Assets:US:Vanguard:Cash -360.00 USD 2017-06-16 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2017-06-19 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 7.552 VBMPX {63.56 USD, 2017-06-19} Assets:US:Vanguard:Cash -480.01 USD 2017-06-19 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.514 RGAGX {68.48 USD, 2017-06-19} Assets:US:Vanguard:Cash -720.00 USD 2017-06-19 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.776 VBMPX {63.56 USD, 2017-06-19} Assets:US:Vanguard:Cash -240.00 USD 2017-06-19 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.257 RGAGX {68.48 USD, 2017-06-19} Assets:US:Vanguard:Cash -360.00 USD 2017-06-30 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2017-07-03 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 7.506 VBMPX {63.95 USD, 2017-07-03} Assets:US:Vanguard:Cash -480.01 USD 2017-07-03 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.880 RGAGX {66.18 USD, 2017-07-03} Assets:US:Vanguard:Cash -720.04 USD 2017-07-03 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.753 VBMPX {63.95 USD, 2017-07-03} Assets:US:Vanguard:Cash -240.00 USD 2017-07-03 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.440 RGAGX {66.18 USD, 2017-07-03} Assets:US:Vanguard:Cash -360.02 USD 2017-07-14 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2017-07-17 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 7.618 VBMPX {63.01 USD, 2017-07-17} Assets:US:Vanguard:Cash -480.01 USD 2017-07-17 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 11.163 RGAGX {64.50 USD, 2017-07-17} Assets:US:Vanguard:Cash -720.01 USD 2017-07-17 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.809 VBMPX {63.01 USD, 2017-07-17} Assets:US:Vanguard:Cash -240.01 USD 2017-07-17 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.581 RGAGX {64.50 USD, 2017-07-17} Assets:US:Vanguard:Cash -359.97 USD 2017-07-28 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:BayBook:Match401k -600.00 USD 2017-07-31 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 7.564 VBMPX {63.46 USD, 2017-07-31} Assets:US:Vanguard:Cash -480.01 USD 2017-07-31 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 10.984 RGAGX {65.55 USD, 2017-07-31} Assets:US:Vanguard:Cash -720.00 USD 2017-07-31 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.782 VBMPX {63.46 USD, 2017-07-31} Assets:US:Vanguard:Cash -240.01 USD 2017-07-31 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 5.492 RGAGX {65.55 USD, 2017-07-31} Assets:US:Vanguard:Cash -360.00 USD 2017-08-11 * "Employer match for contribution" Assets:US:Vanguard:Cash 250.00 USD Income:US:BayBook:Match401k -250.00 USD 2017-08-14 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.152 VBMPX {63.45 USD, 2017-08-14} Assets:US:Vanguard:Cash -199.99 USD 2017-08-14 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.562 RGAGX {65.76 USD, 2017-08-14} Assets:US:Vanguard:Cash -300.00 USD 2017-08-14 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.576 VBMPX {63.45 USD, 2017-08-14} Assets:US:Vanguard:Cash -100.00 USD 2017-08-14 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.281 RGAGX {65.76 USD, 2017-08-14} Assets:US:Vanguard:Cash -150.00 USD * Sources of Income 2015-01-01 open Income:US:BayBook:Salary USD 2015-01-01 open Income:US:BayBook:GroupTermLife USD 2015-01-01 open Income:US:BayBook:Vacation VACHR 2015-01-01 open Assets:US:BayBook:Vacation VACHR 2015-01-01 open Expenses:Vacation VACHR 2015-01-01 open Expenses:Health:Life:GroupTermLife 2015-01-01 open Expenses:Health:Medical:Insurance 2015-01-01 open Expenses:Health:Dental:Insurance 2015-01-01 open Expenses:Health:Vision:Insurance 2015-01-01 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-01-01 event "employer" "BayBook, 1501 Billow Rd, Benlo Park, CA" 2015-01-15 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-01-29 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-02-12 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-02-26 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-03-12 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-03-26 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-04-09 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-04-23 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-05-07 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-05-21 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-06-04 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-06-18 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-07-02 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-07-16 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-07-30 * "BayBook" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-08-13 * "BayBook" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-08-27 * "BayBook" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-09-10 * "BayBook" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-09-24 * "BayBook" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-10-08 * "BayBook" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-10-22 * "BayBook" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-11-05 * "BayBook" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-11-19 * "BayBook" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-12-03 * "BayBook" "Payroll" Assets:US:BofA:Checking 2589.06 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 243.08 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-12-17 * "BayBook" "Payroll" Assets:US:BofA:Checking 2832.14 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 0.00 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2015-12-31 * "BayBook" "Payroll" Assets:US:BofA:Checking 2832.14 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 0.00 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-01-14 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-01-28 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-02-11 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-02-25 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-03-10 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-03-24 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-04-07 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-04-21 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-05-05 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-05-19 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-06-02 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-06-16 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-06-30 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-07-14 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-07-28 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-08-11 * "BayBook" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-08-25 * "BayBook" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-09-08 * "BayBook" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-09-22 * "BayBook" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-10-06 * "BayBook" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-10-20 * "BayBook" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-11-03 * "BayBook" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-11-17 * "BayBook" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-12-01 * "BayBook" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-12-15 * "BayBook" "Payroll" Assets:US:BofA:Checking 2589.06 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 243.08 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2016-12-29 * "BayBook" "Payroll" Assets:US:BofA:Checking 2832.14 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 0.00 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2017-01-12 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2017-01-26 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2017-02-09 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2017-02-23 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2017-03-09 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2017-03-23 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2017-04-06 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2017-04-20 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2017-05-04 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2017-05-18 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2017-06-01 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2017-06-15 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2017-06-29 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2017-07-13 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2017-07-27 * "BayBook" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2017-08-10 * "BayBook" "Payroll" Assets:US:BofA:Checking 2050.60 USD Assets:US:Vanguard:Cash 500.00 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -500.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 500.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2017-08-24 * "BayBook" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR 2017-09-07 * "BayBook" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:BayBook:Salary -4615.38 USD Income:US:BayBook:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:BayBook:Vacation 5 VACHR Income:US:BayBook:Vacation -5 VACHR * Taxes 1980-05-12 open Income:US:Federal:PreTax401k IRAUSD 1980-05-12 open Assets:US:Federal:PreTax401k IRAUSD ** Tax Year 2015 2015-01-01 open Expenses:Taxes:Y2015:US:Federal:PreTax401k IRAUSD 2015-01-01 open Expenses:Taxes:Y2015:US:Medicare USD 2015-01-01 open Expenses:Taxes:Y2015:US:Federal USD 2015-01-01 open Expenses:Taxes:Y2015:US:CityNYC USD 2015-01-01 open Expenses:Taxes:Y2015:US:SDI USD 2015-01-01 open Expenses:Taxes:Y2015:US:State USD 2015-01-01 open Expenses:Taxes:Y2015:US:SocSec USD 2015-01-01 balance Assets:US:Federal:PreTax401k 0 IRAUSD 2015-01-01 * "Allowed contributions for one year" Income:US:Federal:PreTax401k -18000 IRAUSD Assets:US:Federal:PreTax401k 18000 IRAUSD 2016-03-25 * "Filing taxes for 2015" Expenses:Taxes:Y2015:US:Federal 316.96 USD Expenses:Taxes:Y2015:US:State 341.51 USD Liabilities:AccountsPayable -658.47 USD 2016-03-26 * "STATE TAX & FINANC PYMT" Assets:US:BofA:Checking -341.51 USD Liabilities:AccountsPayable 341.51 USD 2016-03-28 * "FEDERAL TAXPYMT" Assets:US:BofA:Checking -316.96 USD Liabilities:AccountsPayable 316.96 USD ** Tax Year 2016 2016-01-01 open Expenses:Taxes:Y2016:US:Federal:PreTax401k IRAUSD 2016-01-01 open Expenses:Taxes:Y2016:US:Medicare USD 2016-01-01 open Expenses:Taxes:Y2016:US:Federal USD 2016-01-01 open Expenses:Taxes:Y2016:US:CityNYC USD 2016-01-01 open Expenses:Taxes:Y2016:US:SDI USD 2016-01-01 open Expenses:Taxes:Y2016:US:State USD 2016-01-01 open Expenses:Taxes:Y2016:US:SocSec USD 2016-01-01 balance Assets:US:Federal:PreTax401k 0 IRAUSD 2016-01-01 * "Allowed contributions for one year" Income:US:Federal:PreTax401k -18000 IRAUSD Assets:US:Federal:PreTax401k 18000 IRAUSD 2017-03-20 * "Filing taxes for 2016" Expenses:Taxes:Y2016:US:Federal 421.57 USD Expenses:Taxes:Y2016:US:State 431.01 USD Liabilities:AccountsPayable -852.58 USD 2017-03-22 * "FEDERAL TAXPYMT" Assets:US:BofA:Checking -421.57 USD Liabilities:AccountsPayable 421.57 USD 2017-03-24 * "STATE TAX & FINANC PYMT" Assets:US:BofA:Checking -431.01 USD Liabilities:AccountsPayable 431.01 USD ** Tax Year 2017 2017-01-01 open Expenses:Taxes:Y2017:US:Federal:PreTax401k IRAUSD 2017-01-01 open Expenses:Taxes:Y2017:US:Medicare USD 2017-01-01 open Expenses:Taxes:Y2017:US:Federal USD 2017-01-01 open Expenses:Taxes:Y2017:US:CityNYC USD 2017-01-01 open Expenses:Taxes:Y2017:US:SDI USD 2017-01-01 open Expenses:Taxes:Y2017:US:State USD 2017-01-01 open Expenses:Taxes:Y2017:US:SocSec USD 2017-01-01 balance Assets:US:Federal:PreTax401k 0 IRAUSD 2017-01-01 * "Allowed contributions for one year" Income:US:Federal:PreTax401k -18500 IRAUSD Assets:US:Federal:PreTax401k 18500 IRAUSD * Expenses 1980-05-12 open Expenses:Food:Groceries 1980-05-12 open Expenses:Food:Restaurant 1980-05-12 open Expenses:Food:Coffee 1980-05-12 open Expenses:Food:Alcohol 1980-05-12 open Expenses:Transport:Tram 1980-05-12 open Expenses:Home:Rent 1980-05-12 open Expenses:Home:Electricity 1980-05-12 open Expenses:Home:Internet 1980-05-12 open Expenses:Home:Phone 1980-05-12 open Expenses:Financial:Fees 1980-05-12 open Expenses:Financial:Commissions * Budgets 2015-01-01 custom "budget" Expenses:Financial:Commissions "monthly" 15.00 USD 2015-01-01 custom "budget" Expenses:Financial:Fees "monthly" 4.00 USD 2015-01-01 custom "budget" Expenses:Food:Alcohol "monthly" 10.00 USD 2015-01-01 custom "budget" Expenses:Food:Coffee "monthly" 10.00 USD 2015-01-01 custom "budget" Expenses:Food:Groceries "monthly" 200.00 USD 2015-01-01 custom "budget" Expenses:Food:Restaurant "monthly" 450.00 USD * Prices 2015-01-02 price VBMPX 49.22 USD 2015-01-02 price RGAGX 65.13 USD 2015-01-02 price ITOT 170.52 USD 2015-01-02 price VEA 171.33 USD 2015-01-02 price VHT 95.25 USD 2015-01-02 price GLD 60.95 USD 2015-01-09 price VBMPX 49.39 USD 2015-01-09 price RGAGX 66.11 USD 2015-01-09 price ITOT 169.77 USD 2015-01-09 price VEA 171.29 USD 2015-01-09 price VHT 94.76 USD 2015-01-09 price GLD 61.69 USD 2015-01-16 price VBMPX 49.54 USD 2015-01-16 price RGAGX 64.62 USD 2015-01-16 price ITOT 170.16 USD 2015-01-16 price VEA 175.22 USD 2015-01-16 price VHT 95.01 USD 2015-01-16 price GLD 62.23 USD 2015-01-23 price VBMPX 49.60 USD 2015-01-23 price RGAGX 62.39 USD 2015-01-23 price ITOT 169.75 USD 2015-01-23 price VEA 176.93 USD 2015-01-23 price VHT 94.78 USD 2015-01-23 price GLD 61.73 USD 2015-01-30 price VBMPX 49.77 USD 2015-01-30 price RGAGX 63.29 USD 2015-01-30 price ITOT 169.59 USD 2015-01-30 price VEA 177.69 USD 2015-01-30 price VHT 93.35 USD 2015-01-30 price GLD 62.65 USD 2015-02-06 price VBMPX 49.97 USD 2015-02-06 price RGAGX 66.32 USD 2015-02-06 price ITOT 171.79 USD 2015-02-06 price VEA 178.62 USD 2015-02-06 price VHT 92.38 USD 2015-02-06 price GLD 63.30 USD 2015-02-13 price VBMPX 49.80 USD 2015-02-13 price RGAGX 66.39 USD 2015-02-13 price ITOT 168.98 USD 2015-02-13 price VEA 176.47 USD 2015-02-13 price VHT 92.24 USD 2015-02-13 price GLD 62.12 USD 2015-02-20 price VBMPX 50.65 USD 2015-02-20 price RGAGX 65.88 USD 2015-02-20 price ITOT 170.58 USD 2015-02-20 price VEA 177.86 USD 2015-02-20 price VHT 92.90 USD 2015-02-20 price GLD 61.92 USD 2015-02-27 price VBMPX 50.95 USD 2015-02-27 price RGAGX 65.34 USD 2015-02-27 price ITOT 172.27 USD 2015-02-27 price VEA 179.11 USD 2015-02-27 price VHT 90.72 USD 2015-02-27 price GLD 61.67 USD 2015-03-06 price VBMPX 51.38 USD 2015-03-06 price RGAGX 66.95 USD 2015-03-06 price ITOT 171.27 USD 2015-03-06 price VEA 180.99 USD 2015-03-06 price VHT 90.83 USD 2015-03-06 price GLD 60.21 USD 2015-03-13 price VBMPX 51.11 USD 2015-03-13 price RGAGX 67.57 USD 2015-03-13 price ITOT 171.41 USD 2015-03-13 price VEA 178.84 USD 2015-03-13 price VHT 90.13 USD 2015-03-13 price GLD 59.92 USD 2015-03-20 price VBMPX 52.19 USD 2015-03-20 price RGAGX 67.08 USD 2015-03-20 price ITOT 170.51 USD 2015-03-20 price VEA 177.61 USD 2015-03-20 price VHT 89.43 USD 2015-03-20 price GLD 61.06 USD 2015-03-27 price VBMPX 52.42 USD 2015-03-27 price RGAGX 66.33 USD 2015-03-27 price ITOT 170.41 USD 2015-03-27 price VEA 178.52 USD 2015-03-27 price VHT 90.97 USD 2015-03-27 price GLD 61.02 USD 2015-04-03 price VBMPX 52.66 USD 2015-04-03 price RGAGX 66.77 USD 2015-04-03 price ITOT 170.93 USD 2015-04-03 price VEA 180.07 USD 2015-04-03 price VHT 91.56 USD 2015-04-03 price GLD 60.91 USD 2015-04-10 price VBMPX 52.50 USD 2015-04-10 price RGAGX 68.25 USD 2015-04-10 price ITOT 170.13 USD 2015-04-10 price VEA 182.20 USD 2015-04-10 price VHT 94.09 USD 2015-04-10 price GLD 60.97 USD 2015-04-17 price VBMPX 52.31 USD 2015-04-17 price RGAGX 68.00 USD 2015-04-17 price ITOT 170.56 USD 2015-04-17 price VEA 182.63 USD 2015-04-17 price VHT 94.49 USD 2015-04-17 price GLD 60.73 USD 2015-04-24 price VBMPX 52.92 USD 2015-04-24 price RGAGX 66.30 USD 2015-04-24 price ITOT 169.15 USD 2015-04-24 price VEA 182.47 USD 2015-04-24 price VHT 94.78 USD 2015-04-24 price GLD 60.38 USD 2015-05-01 price VBMPX 52.82 USD 2015-05-01 price RGAGX 65.03 USD 2015-05-01 price ITOT 168.29 USD 2015-05-01 price VEA 183.31 USD 2015-05-01 price VHT 92.51 USD 2015-05-01 price GLD 61.37 USD 2015-05-08 price VBMPX 53.13 USD 2015-05-08 price RGAGX 65.31 USD 2015-05-08 price ITOT 168.84 USD 2015-05-08 price VEA 184.48 USD 2015-05-08 price VHT 92.38 USD 2015-05-08 price GLD 62.28 USD 2015-05-15 price VBMPX 53.08 USD 2015-05-15 price RGAGX 64.36 USD 2015-05-15 price ITOT 170.36 USD 2015-05-15 price VEA 185.70 USD 2015-05-15 price VHT 91.67 USD 2015-05-15 price GLD 61.90 USD 2015-05-22 price VBMPX 53.06 USD 2015-05-22 price RGAGX 62.66 USD 2015-05-22 price ITOT 169.29 USD 2015-05-22 price VEA 188.22 USD 2015-05-22 price VHT 90.33 USD 2015-05-22 price GLD 62.97 USD 2015-05-29 price VBMPX 52.91 USD 2015-05-29 price RGAGX 65.72 USD 2015-05-29 price ITOT 169.82 USD 2015-05-29 price VEA 188.15 USD 2015-05-29 price VHT 90.04 USD 2015-05-29 price GLD 62.95 USD 2015-06-05 price VBMPX 53.53 USD 2015-06-05 price RGAGX 66.43 USD 2015-06-05 price ITOT 169.00 USD 2015-06-05 price VEA 189.86 USD 2015-06-05 price VHT 90.08 USD 2015-06-05 price GLD 63.39 USD 2015-06-12 price VBMPX 53.59 USD 2015-06-12 price RGAGX 65.35 USD 2015-06-12 price ITOT 167.64 USD 2015-06-12 price VEA 191.07 USD 2015-06-12 price VHT 89.62 USD 2015-06-12 price GLD 64.32 USD 2015-06-19 price VBMPX 53.17 USD 2015-06-19 price RGAGX 65.25 USD 2015-06-19 price ITOT 168.42 USD 2015-06-19 price VEA 189.74 USD 2015-06-19 price VHT 90.07 USD 2015-06-19 price GLD 65.82 USD 2015-06-26 price VBMPX 53.00 USD 2015-06-26 price RGAGX 64.12 USD 2015-06-26 price ITOT 165.46 USD 2015-06-26 price VEA 192.10 USD 2015-06-26 price VHT 91.58 USD 2015-06-26 price GLD 65.34 USD 2015-07-03 price VBMPX 53.30 USD 2015-07-03 price RGAGX 65.70 USD 2015-07-03 price ITOT 166.06 USD 2015-07-03 price VEA 191.73 USD 2015-07-03 price VHT 91.91 USD 2015-07-03 price GLD 64.61 USD 2015-07-10 price VBMPX 53.36 USD 2015-07-10 price RGAGX 66.57 USD 2015-07-10 price ITOT 166.67 USD 2015-07-10 price VEA 192.72 USD 2015-07-10 price VHT 93.32 USD 2015-07-10 price GLD 65.91 USD 2015-07-17 price VBMPX 53.86 USD 2015-07-17 price RGAGX 65.82 USD 2015-07-17 price ITOT 166.31 USD 2015-07-17 price VEA 193.72 USD 2015-07-17 price VHT 93.25 USD 2015-07-17 price GLD 66.04 USD 2015-07-24 price VBMPX 54.03 USD 2015-07-24 price RGAGX 67.69 USD 2015-07-24 price ITOT 164.10 USD 2015-07-24 price VEA 193.27 USD 2015-07-24 price VHT 94.23 USD 2015-07-24 price GLD 66.41 USD 2015-07-31 price VBMPX 53.69 USD 2015-07-31 price RGAGX 69.35 USD 2015-07-31 price ITOT 165.00 USD 2015-07-31 price VEA 193.37 USD 2015-07-31 price VHT 94.55 USD 2015-07-31 price GLD 65.86 USD 2015-08-07 price VBMPX 53.96 USD 2015-08-07 price RGAGX 69.23 USD 2015-08-07 price ITOT 165.20 USD 2015-08-07 price VEA 194.59 USD 2015-08-07 price VHT 92.34 USD 2015-08-07 price GLD 64.30 USD 2015-08-14 price VBMPX 54.02 USD 2015-08-14 price RGAGX 69.12 USD 2015-08-14 price ITOT 167.22 USD 2015-08-14 price VEA 195.61 USD 2015-08-14 price VHT 92.09 USD 2015-08-14 price GLD 62.71 USD 2015-08-21 price VBMPX 54.50 USD 2015-08-21 price RGAGX 67.89 USD 2015-08-21 price ITOT 167.50 USD 2015-08-21 price VEA 195.42 USD 2015-08-21 price VHT 92.79 USD 2015-08-21 price GLD 62.76 USD 2015-08-28 price VBMPX 54.96 USD 2015-08-28 price RGAGX 65.36 USD 2015-08-28 price ITOT 166.99 USD 2015-08-28 price VEA 194.13 USD 2015-08-28 price VHT 93.01 USD 2015-08-28 price GLD 62.60 USD 2015-09-04 price VBMPX 55.37 USD 2015-09-04 price RGAGX 65.74 USD 2015-09-04 price ITOT 166.64 USD 2015-09-04 price VEA 194.65 USD 2015-09-04 price VHT 96.99 USD 2015-09-04 price GLD 60.92 USD 2015-09-11 price VBMPX 56.26 USD 2015-09-11 price RGAGX 66.38 USD 2015-09-11 price ITOT 166.55 USD 2015-09-11 price VEA 194.96 USD 2015-09-11 price VHT 97.25 USD 2015-09-11 price GLD 60.55 USD 2015-09-18 price VBMPX 56.66 USD 2015-09-18 price RGAGX 68.32 USD 2015-09-18 price ITOT 167.35 USD 2015-09-18 price VEA 193.86 USD 2015-09-18 price VHT 96.27 USD 2015-09-18 price GLD 62.21 USD 2015-09-25 price VBMPX 56.52 USD 2015-09-25 price RGAGX 67.97 USD 2015-09-25 price ITOT 167.15 USD 2015-09-25 price VEA 193.64 USD 2015-09-25 price VHT 95.09 USD 2015-09-25 price GLD 62.65 USD 2015-10-02 price VBMPX 57.16 USD 2015-10-02 price RGAGX 67.23 USD 2015-10-02 price ITOT 170.09 USD 2015-10-02 price VEA 193.79 USD 2015-10-02 price VHT 95.30 USD 2015-10-02 price GLD 62.46 USD 2015-10-09 price VBMPX 57.61 USD 2015-10-09 price RGAGX 68.81 USD 2015-10-09 price ITOT 173.52 USD 2015-10-09 price VEA 193.24 USD 2015-10-09 price VHT 95.99 USD 2015-10-09 price GLD 62.69 USD 2015-10-16 price VBMPX 57.82 USD 2015-10-16 price RGAGX 68.63 USD 2015-10-16 price ITOT 173.99 USD 2015-10-16 price VEA 194.75 USD 2015-10-16 price VHT 93.96 USD 2015-10-16 price GLD 61.70 USD 2015-10-23 price VBMPX 57.79 USD 2015-10-23 price RGAGX 67.97 USD 2015-10-23 price ITOT 176.32 USD 2015-10-23 price VEA 194.05 USD 2015-10-23 price VHT 92.11 USD 2015-10-23 price GLD 61.67 USD 2015-10-30 price VBMPX 58.73 USD 2015-10-30 price RGAGX 67.48 USD 2015-10-30 price ITOT 174.81 USD 2015-10-30 price VEA 197.63 USD 2015-10-30 price VHT 91.71 USD 2015-10-30 price GLD 60.89 USD 2015-11-06 price VBMPX 59.12 USD 2015-11-06 price RGAGX 67.11 USD 2015-11-06 price ITOT 174.70 USD 2015-11-06 price VEA 198.16 USD 2015-11-06 price VHT 93.77 USD 2015-11-06 price GLD 61.10 USD 2015-11-13 price VBMPX 58.99 USD 2015-11-13 price RGAGX 66.06 USD 2015-11-13 price ITOT 172.06 USD 2015-11-13 price VEA 197.13 USD 2015-11-13 price VHT 93.17 USD 2015-11-13 price GLD 61.13 USD 2015-11-20 price VBMPX 59.10 USD 2015-11-20 price RGAGX 67.63 USD 2015-11-20 price ITOT 169.60 USD 2015-11-20 price VEA 196.55 USD 2015-11-20 price VHT 92.52 USD 2015-11-20 price GLD 60.32 USD 2015-11-27 price VBMPX 59.42 USD 2015-11-27 price RGAGX 65.70 USD 2015-11-27 price ITOT 169.47 USD 2015-11-27 price VEA 196.36 USD 2015-11-27 price VHT 93.23 USD 2015-11-27 price GLD 60.70 USD 2015-12-04 price VBMPX 59.88 USD 2015-12-04 price RGAGX 64.33 USD 2015-12-04 price ITOT 170.64 USD 2015-12-04 price VEA 197.03 USD 2015-12-04 price VHT 94.76 USD 2015-12-04 price GLD 60.50 USD 2015-12-11 price VBMPX 60.46 USD 2015-12-11 price RGAGX 65.54 USD 2015-12-11 price ITOT 172.27 USD 2015-12-11 price VEA 197.52 USD 2015-12-11 price VHT 96.12 USD 2015-12-11 price GLD 61.69 USD 2015-12-18 price VBMPX 60.43 USD 2015-12-18 price RGAGX 64.11 USD 2015-12-18 price ITOT 170.84 USD 2015-12-18 price VEA 198.59 USD 2015-12-18 price VHT 96.64 USD 2015-12-18 price GLD 60.40 USD 2015-12-25 price VBMPX 60.15 USD 2015-12-25 price RGAGX 65.59 USD 2015-12-25 price ITOT 168.47 USD 2015-12-25 price VEA 199.00 USD 2015-12-25 price VHT 96.44 USD 2015-12-25 price GLD 60.19 USD 2016-01-01 price VBMPX 59.53 USD 2016-01-01 price RGAGX 68.57 USD 2016-01-01 price ITOT 170.76 USD 2016-01-01 price VEA 198.92 USD 2016-01-01 price VHT 97.88 USD 2016-01-01 price GLD 58.30 USD 2016-01-08 price VBMPX 59.37 USD 2016-01-08 price RGAGX 67.95 USD 2016-01-08 price ITOT 173.10 USD 2016-01-08 price VEA 199.03 USD 2016-01-08 price VHT 96.65 USD 2016-01-08 price GLD 57.89 USD 2016-01-15 price VBMPX 58.97 USD 2016-01-15 price RGAGX 68.44 USD 2016-01-15 price ITOT 173.71 USD 2016-01-15 price VEA 199.31 USD 2016-01-15 price VHT 97.37 USD 2016-01-15 price GLD 57.85 USD 2016-01-22 price VBMPX 59.23 USD 2016-01-22 price RGAGX 69.56 USD 2016-01-22 price ITOT 174.73 USD 2016-01-22 price VEA 200.48 USD 2016-01-22 price VHT 98.06 USD 2016-01-22 price GLD 57.92 USD 2016-01-29 price VBMPX 59.27 USD 2016-01-29 price RGAGX 71.39 USD 2016-01-29 price ITOT 174.66 USD 2016-01-29 price VEA 199.35 USD 2016-01-29 price VHT 97.21 USD 2016-01-29 price GLD 58.18 USD 2016-02-05 price VBMPX 58.58 USD 2016-02-05 price RGAGX 73.19 USD 2016-02-05 price ITOT 174.24 USD 2016-02-05 price VEA 199.07 USD 2016-02-05 price VHT 96.78 USD 2016-02-05 price GLD 59.57 USD 2016-02-12 price VBMPX 58.80 USD 2016-02-12 price RGAGX 71.56 USD 2016-02-12 price ITOT 175.35 USD 2016-02-12 price VEA 200.19 USD 2016-02-12 price VHT 97.23 USD 2016-02-12 price GLD 58.89 USD 2016-02-19 price VBMPX 58.22 USD 2016-02-19 price RGAGX 72.38 USD 2016-02-19 price ITOT 176.86 USD 2016-02-19 price VEA 198.75 USD 2016-02-19 price VHT 97.92 USD 2016-02-19 price GLD 58.24 USD 2016-02-26 price VBMPX 58.29 USD 2016-02-26 price RGAGX 72.06 USD 2016-02-26 price ITOT 176.10 USD 2016-02-26 price VEA 195.55 USD 2016-02-26 price VHT 98.82 USD 2016-02-26 price GLD 58.86 USD 2016-03-04 price VBMPX 58.42 USD 2016-03-04 price RGAGX 72.66 USD 2016-03-04 price ITOT 175.30 USD 2016-03-04 price VEA 196.10 USD 2016-03-04 price VHT 97.47 USD 2016-03-04 price GLD 58.61 USD 2016-03-11 price VBMPX 58.66 USD 2016-03-11 price RGAGX 75.72 USD 2016-03-11 price ITOT 177.85 USD 2016-03-11 price VEA 195.95 USD 2016-03-11 price VHT 97.40 USD 2016-03-11 price GLD 59.48 USD 2016-03-18 price VBMPX 58.67 USD 2016-03-18 price RGAGX 78.50 USD 2016-03-18 price ITOT 178.54 USD 2016-03-18 price VEA 195.29 USD 2016-03-18 price VHT 98.10 USD 2016-03-18 price GLD 58.97 USD 2016-03-25 price VBMPX 58.40 USD 2016-03-25 price RGAGX 76.17 USD 2016-03-25 price ITOT 177.28 USD 2016-03-25 price VEA 192.59 USD 2016-03-25 price VHT 98.64 USD 2016-03-25 price GLD 59.12 USD 2016-04-01 price VBMPX 58.68 USD 2016-04-01 price RGAGX 76.67 USD 2016-04-01 price ITOT 179.14 USD 2016-04-01 price VEA 193.48 USD 2016-04-01 price VHT 97.59 USD 2016-04-01 price GLD 59.24 USD 2016-04-08 price VBMPX 58.59 USD 2016-04-08 price RGAGX 77.46 USD 2016-04-08 price ITOT 180.27 USD 2016-04-08 price VEA 196.66 USD 2016-04-08 price VHT 97.28 USD 2016-04-08 price GLD 60.81 USD 2016-04-15 price VBMPX 59.56 USD 2016-04-15 price RGAGX 77.85 USD 2016-04-15 price ITOT 180.23 USD 2016-04-15 price VEA 196.50 USD 2016-04-15 price VHT 96.47 USD 2016-04-15 price GLD 60.36 USD 2016-04-22 price VBMPX 59.33 USD 2016-04-22 price RGAGX 75.89 USD 2016-04-22 price ITOT 180.26 USD 2016-04-22 price VEA 194.41 USD 2016-04-22 price VHT 96.55 USD 2016-04-22 price GLD 60.16 USD 2016-04-29 price VBMPX 59.57 USD 2016-04-29 price RGAGX 74.01 USD 2016-04-29 price ITOT 178.39 USD 2016-04-29 price VEA 194.90 USD 2016-04-29 price VHT 96.88 USD 2016-04-29 price GLD 59.70 USD 2016-05-06 price VBMPX 59.39 USD 2016-05-06 price RGAGX 75.25 USD 2016-05-06 price ITOT 178.36 USD 2016-05-06 price VEA 191.61 USD 2016-05-06 price VHT 96.01 USD 2016-05-06 price GLD 59.98 USD 2016-05-13 price VBMPX 59.81 USD 2016-05-13 price RGAGX 74.79 USD 2016-05-13 price ITOT 177.55 USD 2016-05-13 price VEA 191.81 USD 2016-05-13 price VHT 96.53 USD 2016-05-13 price GLD 59.79 USD 2016-05-20 price VBMPX 59.94 USD 2016-05-20 price RGAGX 72.16 USD 2016-05-20 price ITOT 176.43 USD 2016-05-20 price VEA 189.86 USD 2016-05-20 price VHT 97.57 USD 2016-05-20 price GLD 60.09 USD 2016-05-27 price VBMPX 60.41 USD 2016-05-27 price RGAGX 71.19 USD 2016-05-27 price ITOT 174.15 USD 2016-05-27 price VEA 188.07 USD 2016-05-27 price VHT 98.75 USD 2016-05-27 price GLD 61.07 USD 2016-06-03 price VBMPX 60.45 USD 2016-06-03 price RGAGX 70.68 USD 2016-06-03 price ITOT 172.55 USD 2016-06-03 price VEA 186.23 USD 2016-06-03 price VHT 97.91 USD 2016-06-03 price GLD 61.23 USD 2016-06-10 price VBMPX 60.61 USD 2016-06-10 price RGAGX 66.72 USD 2016-06-10 price ITOT 174.60 USD 2016-06-10 price VEA 183.66 USD 2016-06-10 price VHT 98.80 USD 2016-06-10 price GLD 61.44 USD 2016-06-17 price VBMPX 60.17 USD 2016-06-17 price RGAGX 65.81 USD 2016-06-17 price ITOT 174.04 USD 2016-06-17 price VEA 182.93 USD 2016-06-17 price VHT 98.95 USD 2016-06-17 price GLD 60.09 USD 2016-06-24 price VBMPX 60.22 USD 2016-06-24 price RGAGX 65.70 USD 2016-06-24 price ITOT 176.57 USD 2016-06-24 price VEA 182.34 USD 2016-06-24 price VHT 97.13 USD 2016-06-24 price GLD 60.77 USD 2016-07-01 price VBMPX 60.73 USD 2016-07-01 price RGAGX 64.76 USD 2016-07-01 price ITOT 175.18 USD 2016-07-01 price VEA 181.56 USD 2016-07-01 price VHT 96.17 USD 2016-07-01 price GLD 61.15 USD 2016-07-08 price VBMPX 60.97 USD 2016-07-08 price RGAGX 66.28 USD 2016-07-08 price ITOT 176.48 USD 2016-07-08 price VEA 182.36 USD 2016-07-08 price VHT 94.47 USD 2016-07-08 price GLD 60.84 USD 2016-07-15 price VBMPX 60.95 USD 2016-07-15 price RGAGX 67.74 USD 2016-07-15 price ITOT 176.02 USD 2016-07-15 price VEA 184.74 USD 2016-07-15 price VHT 93.67 USD 2016-07-15 price GLD 60.96 USD 2016-07-22 price VBMPX 60.90 USD 2016-07-22 price RGAGX 66.52 USD 2016-07-22 price ITOT 176.68 USD 2016-07-22 price VEA 186.11 USD 2016-07-22 price VHT 94.93 USD 2016-07-22 price GLD 61.33 USD 2016-07-29 price VBMPX 60.67 USD 2016-07-29 price RGAGX 68.12 USD 2016-07-29 price ITOT 176.22 USD 2016-07-29 price VEA 188.71 USD 2016-07-29 price VHT 96.57 USD 2016-07-29 price GLD 62.20 USD 2016-08-05 price VBMPX 61.25 USD 2016-08-05 price RGAGX 66.59 USD 2016-08-05 price ITOT 178.22 USD 2016-08-05 price VEA 188.04 USD 2016-08-05 price VHT 97.12 USD 2016-08-05 price GLD 62.45 USD 2016-08-12 price VBMPX 60.94 USD 2016-08-12 price RGAGX 65.32 USD 2016-08-12 price ITOT 176.90 USD 2016-08-12 price VEA 187.13 USD 2016-08-12 price VHT 98.01 USD 2016-08-12 price GLD 62.87 USD 2016-08-19 price VBMPX 61.10 USD 2016-08-19 price RGAGX 65.75 USD 2016-08-19 price ITOT 177.92 USD 2016-08-19 price VEA 186.61 USD 2016-08-19 price VHT 98.77 USD 2016-08-19 price GLD 62.36 USD 2016-08-26 price VBMPX 61.07 USD 2016-08-26 price RGAGX 66.51 USD 2016-08-26 price ITOT 178.76 USD 2016-08-26 price VEA 187.37 USD 2016-08-26 price VHT 99.54 USD 2016-08-26 price GLD 62.45 USD 2016-09-02 price VBMPX 61.12 USD 2016-09-02 price RGAGX 67.12 USD 2016-09-02 price ITOT 179.86 USD 2016-09-02 price VEA 189.48 USD 2016-09-02 price VHT 99.40 USD 2016-09-02 price GLD 63.64 USD 2016-09-09 price VBMPX 61.10 USD 2016-09-09 price RGAGX 69.79 USD 2016-09-09 price ITOT 178.14 USD 2016-09-09 price VEA 188.39 USD 2016-09-09 price VHT 98.31 USD 2016-09-09 price GLD 63.59 USD 2016-09-16 price VBMPX 61.61 USD 2016-09-16 price RGAGX 70.10 USD 2016-09-16 price ITOT 178.33 USD 2016-09-16 price VEA 193.59 USD 2016-09-16 price VHT 97.93 USD 2016-09-16 price GLD 63.21 USD 2016-09-23 price VBMPX 61.37 USD 2016-09-23 price RGAGX 70.59 USD 2016-09-23 price ITOT 178.18 USD 2016-09-23 price VEA 194.32 USD 2016-09-23 price VHT 99.41 USD 2016-09-23 price GLD 62.68 USD 2016-09-30 price VBMPX 61.61 USD 2016-09-30 price RGAGX 69.85 USD 2016-09-30 price ITOT 179.68 USD 2016-09-30 price VEA 195.79 USD 2016-09-30 price VHT 98.81 USD 2016-09-30 price GLD 62.62 USD 2016-10-07 price VBMPX 61.03 USD 2016-10-07 price RGAGX 70.28 USD 2016-10-07 price ITOT 178.59 USD 2016-10-07 price VEA 193.90 USD 2016-10-07 price VHT 99.97 USD 2016-10-07 price GLD 62.51 USD 2016-10-14 price VBMPX 60.53 USD 2016-10-14 price RGAGX 70.34 USD 2016-10-14 price ITOT 178.66 USD 2016-10-14 price VEA 193.66 USD 2016-10-14 price VHT 101.55 USD 2016-10-14 price GLD 62.31 USD 2016-10-21 price VBMPX 60.52 USD 2016-10-21 price RGAGX 70.54 USD 2016-10-21 price ITOT 179.65 USD 2016-10-21 price VEA 192.60 USD 2016-10-21 price VHT 102.12 USD 2016-10-21 price GLD 63.06 USD 2016-10-28 price VBMPX 60.47 USD 2016-10-28 price RGAGX 71.07 USD 2016-10-28 price ITOT 182.04 USD 2016-10-28 price VEA 194.00 USD 2016-10-28 price VHT 101.05 USD 2016-10-28 price GLD 62.50 USD 2016-11-04 price VBMPX 60.96 USD 2016-11-04 price RGAGX 69.29 USD 2016-11-04 price ITOT 183.76 USD 2016-11-04 price VEA 194.08 USD 2016-11-04 price VHT 100.27 USD 2016-11-04 price GLD 62.41 USD 2016-11-11 price VBMPX 60.78 USD 2016-11-11 price RGAGX 70.87 USD 2016-11-11 price ITOT 183.97 USD 2016-11-11 price VEA 194.73 USD 2016-11-11 price VHT 101.96 USD 2016-11-11 price GLD 63.47 USD 2016-11-18 price VBMPX 60.69 USD 2016-11-18 price RGAGX 69.01 USD 2016-11-18 price ITOT 184.85 USD 2016-11-18 price VEA 192.46 USD 2016-11-18 price VHT 104.39 USD 2016-11-18 price GLD 64.55 USD 2016-11-25 price VBMPX 60.25 USD 2016-11-25 price RGAGX 68.40 USD 2016-11-25 price ITOT 185.26 USD 2016-11-25 price VEA 191.75 USD 2016-11-25 price VHT 104.67 USD 2016-11-25 price GLD 64.61 USD 2016-12-02 price VBMPX 60.34 USD 2016-12-02 price RGAGX 67.11 USD 2016-12-02 price ITOT 185.49 USD 2016-12-02 price VEA 194.44 USD 2016-12-02 price VHT 104.32 USD 2016-12-02 price GLD 64.74 USD 2016-12-09 price VBMPX 60.74 USD 2016-12-09 price RGAGX 67.95 USD 2016-12-09 price ITOT 184.50 USD 2016-12-09 price VEA 196.51 USD 2016-12-09 price VHT 103.09 USD 2016-12-09 price GLD 65.28 USD 2016-12-16 price VBMPX 61.07 USD 2016-12-16 price RGAGX 66.93 USD 2016-12-16 price ITOT 184.81 USD 2016-12-16 price VEA 196.47 USD 2016-12-16 price VHT 102.97 USD 2016-12-16 price GLD 65.96 USD 2016-12-23 price VBMPX 61.18 USD 2016-12-23 price RGAGX 65.55 USD 2016-12-23 price ITOT 184.46 USD 2016-12-23 price VEA 196.62 USD 2016-12-23 price VHT 104.80 USD 2016-12-23 price GLD 66.27 USD 2016-12-30 price VBMPX 61.88 USD 2016-12-30 price RGAGX 67.90 USD 2016-12-30 price ITOT 185.67 USD 2016-12-30 price VEA 196.78 USD 2016-12-30 price VHT 106.27 USD 2016-12-30 price GLD 65.40 USD 2017-01-06 price VBMPX 61.78 USD 2017-01-06 price RGAGX 68.43 USD 2017-01-06 price ITOT 186.57 USD 2017-01-06 price VEA 198.65 USD 2017-01-06 price VHT 105.99 USD 2017-01-06 price GLD 65.78 USD 2017-01-13 price VBMPX 61.24 USD 2017-01-13 price RGAGX 67.80 USD 2017-01-13 price ITOT 187.23 USD 2017-01-13 price VEA 201.31 USD 2017-01-13 price VHT 107.72 USD 2017-01-13 price GLD 67.78 USD 2017-01-20 price VBMPX 61.48 USD 2017-01-20 price RGAGX 70.66 USD 2017-01-20 price ITOT 186.85 USD 2017-01-20 price VEA 205.22 USD 2017-01-20 price VHT 106.55 USD 2017-01-20 price GLD 69.28 USD 2017-01-27 price VBMPX 61.33 USD 2017-01-27 price RGAGX 69.21 USD 2017-01-27 price ITOT 186.30 USD 2017-01-27 price VEA 209.53 USD 2017-01-27 price VHT 106.37 USD 2017-01-27 price GLD 70.59 USD 2017-02-03 price VBMPX 60.83 USD 2017-02-03 price RGAGX 68.39 USD 2017-02-03 price ITOT 188.11 USD 2017-02-03 price VEA 208.14 USD 2017-02-03 price VHT 108.56 USD 2017-02-03 price GLD 69.20 USD 2017-02-10 price VBMPX 61.01 USD 2017-02-10 price RGAGX 67.03 USD 2017-02-10 price ITOT 184.19 USD 2017-02-10 price VEA 206.18 USD 2017-02-10 price VHT 107.09 USD 2017-02-10 price GLD 69.22 USD 2017-02-17 price VBMPX 61.63 USD 2017-02-17 price RGAGX 66.63 USD 2017-02-17 price ITOT 180.42 USD 2017-02-17 price VEA 206.64 USD 2017-02-17 price VHT 108.36 USD 2017-02-17 price GLD 69.93 USD 2017-02-24 price VBMPX 62.28 USD 2017-02-24 price RGAGX 69.20 USD 2017-02-24 price ITOT 180.60 USD 2017-02-24 price VEA 207.33 USD 2017-02-24 price VHT 108.47 USD 2017-02-24 price GLD 70.95 USD 2017-03-03 price VBMPX 62.79 USD 2017-03-03 price RGAGX 69.97 USD 2017-03-03 price ITOT 179.71 USD 2017-03-03 price VEA 204.38 USD 2017-03-03 price VHT 109.22 USD 2017-03-03 price GLD 70.34 USD 2017-03-10 price VBMPX 62.52 USD 2017-03-10 price RGAGX 70.71 USD 2017-03-10 price ITOT 178.24 USD 2017-03-10 price VEA 205.83 USD 2017-03-10 price VHT 108.93 USD 2017-03-10 price GLD 71.02 USD 2017-03-17 price VBMPX 62.82 USD 2017-03-17 price RGAGX 72.16 USD 2017-03-17 price ITOT 180.13 USD 2017-03-17 price VEA 207.62 USD 2017-03-17 price VHT 107.45 USD 2017-03-17 price GLD 71.61 USD 2017-03-24 price VBMPX 62.64 USD 2017-03-24 price RGAGX 73.86 USD 2017-03-24 price ITOT 179.53 USD 2017-03-24 price VEA 209.13 USD 2017-03-24 price VHT 107.71 USD 2017-03-24 price GLD 72.47 USD 2017-03-31 price VBMPX 62.20 USD 2017-03-31 price RGAGX 72.98 USD 2017-03-31 price ITOT 181.13 USD 2017-03-31 price VEA 211.21 USD 2017-03-31 price VHT 108.09 USD 2017-03-31 price GLD 72.35 USD 2017-04-07 price VBMPX 61.60 USD 2017-04-07 price RGAGX 71.59 USD 2017-04-07 price ITOT 182.13 USD 2017-04-07 price VEA 210.70 USD 2017-04-07 price VHT 108.74 USD 2017-04-07 price GLD 71.60 USD 2017-04-14 price VBMPX 61.97 USD 2017-04-14 price RGAGX 70.57 USD 2017-04-14 price ITOT 182.12 USD 2017-04-14 price VEA 212.82 USD 2017-04-14 price VHT 110.87 USD 2017-04-14 price GLD 72.56 USD 2017-04-21 price VBMPX 62.19 USD 2017-04-21 price RGAGX 68.79 USD 2017-04-21 price ITOT 181.02 USD 2017-04-21 price VEA 214.01 USD 2017-04-21 price VHT 111.73 USD 2017-04-21 price GLD 71.78 USD 2017-04-28 price VBMPX 62.59 USD 2017-04-28 price RGAGX 68.71 USD 2017-04-28 price ITOT 180.93 USD 2017-04-28 price VEA 214.85 USD 2017-04-28 price VHT 109.29 USD 2017-04-28 price GLD 71.58 USD 2017-05-05 price VBMPX 62.57 USD 2017-05-05 price RGAGX 69.65 USD 2017-05-05 price ITOT 182.59 USD 2017-05-05 price VEA 216.61 USD 2017-05-05 price VHT 108.94 USD 2017-05-05 price GLD 72.40 USD 2017-05-12 price VBMPX 62.22 USD 2017-05-12 price RGAGX 70.77 USD 2017-05-12 price ITOT 182.02 USD 2017-05-12 price VEA 217.48 USD 2017-05-12 price VHT 110.94 USD 2017-05-12 price GLD 72.82 USD 2017-05-19 price VBMPX 62.71 USD 2017-05-19 price RGAGX 70.47 USD 2017-05-19 price ITOT 182.03 USD 2017-05-19 price VEA 217.66 USD 2017-05-19 price VHT 111.21 USD 2017-05-19 price GLD 74.30 USD 2017-05-26 price VBMPX 62.86 USD 2017-05-26 price RGAGX 69.52 USD 2017-05-26 price ITOT 180.64 USD 2017-05-26 price VEA 217.77 USD 2017-05-26 price VHT 112.15 USD 2017-05-26 price GLD 73.53 USD 2017-06-02 price VBMPX 62.53 USD 2017-06-02 price RGAGX 70.95 USD 2017-06-02 price ITOT 180.48 USD 2017-06-02 price VEA 217.45 USD 2017-06-02 price VHT 112.11 USD 2017-06-02 price GLD 73.72 USD 2017-06-09 price VBMPX 63.19 USD 2017-06-09 price RGAGX 70.42 USD 2017-06-09 price ITOT 180.29 USD 2017-06-09 price VEA 218.42 USD 2017-06-09 price VHT 110.82 USD 2017-06-09 price GLD 73.91 USD 2017-06-16 price VBMPX 63.56 USD 2017-06-16 price RGAGX 68.48 USD 2017-06-16 price ITOT 180.68 USD 2017-06-16 price VEA 218.50 USD 2017-06-16 price VHT 111.71 USD 2017-06-16 price GLD 74.43 USD 2017-06-23 price VBMPX 63.57 USD 2017-06-23 price RGAGX 67.29 USD 2017-06-23 price ITOT 182.26 USD 2017-06-23 price VEA 221.89 USD 2017-06-23 price VHT 112.24 USD 2017-06-23 price GLD 73.40 USD 2017-06-30 price VBMPX 63.95 USD 2017-06-30 price RGAGX 66.18 USD 2017-06-30 price ITOT 181.11 USD 2017-06-30 price VEA 223.22 USD 2017-06-30 price VHT 111.86 USD 2017-06-30 price GLD 73.81 USD 2017-07-07 price VBMPX 63.89 USD 2017-07-07 price RGAGX 65.36 USD 2017-07-07 price ITOT 183.36 USD 2017-07-07 price VEA 223.53 USD 2017-07-07 price VHT 113.27 USD 2017-07-07 price GLD 73.98 USD 2017-07-14 price VBMPX 63.01 USD 2017-07-14 price RGAGX 64.50 USD 2017-07-14 price ITOT 183.03 USD 2017-07-14 price VEA 223.71 USD 2017-07-14 price VHT 115.56 USD 2017-07-14 price GLD 74.88 USD 2017-07-21 price VBMPX 62.75 USD 2017-07-21 price RGAGX 64.88 USD 2017-07-21 price ITOT 184.56 USD 2017-07-21 price VEA 223.70 USD 2017-07-21 price VHT 116.46 USD 2017-07-21 price GLD 73.99 USD 2017-07-28 price VBMPX 63.46 USD 2017-07-28 price RGAGX 65.55 USD 2017-07-28 price ITOT 185.89 USD 2017-07-28 price VEA 227.00 USD 2017-07-28 price VHT 116.73 USD 2017-07-28 price GLD 74.50 USD 2017-08-04 price VBMPX 63.48 USD 2017-08-04 price RGAGX 64.31 USD 2017-08-04 price ITOT 184.36 USD 2017-08-04 price VEA 228.73 USD 2017-08-04 price VHT 117.19 USD 2017-08-04 price GLD 73.91 USD 2017-08-11 price VBMPX 63.45 USD 2017-08-11 price RGAGX 65.76 USD 2017-08-11 price ITOT 183.79 USD 2017-08-11 price VEA 229.12 USD 2017-08-11 price VHT 116.19 USD 2017-08-11 price GLD 76.49 USD 2017-08-18 price VBMPX 63.29 USD 2017-08-18 price RGAGX 66.07 USD 2017-08-18 price ITOT 184.23 USD 2017-08-18 price VEA 230.50 USD 2017-08-18 price VHT 117.01 USD 2017-08-18 price GLD 76.78 USD 2017-08-25 price VBMPX 63.99 USD 2017-08-25 price RGAGX 65.98 USD 2017-08-25 price ITOT 184.62 USD 2017-08-25 price VEA 228.90 USD 2017-08-25 price VHT 118.16 USD 2017-08-25 price GLD 75.65 USD 2017-09-01 price VBMPX 63.96 USD 2017-09-01 price RGAGX 67.20 USD 2017-09-01 price ITOT 185.27 USD 2017-09-01 price VEA 231.26 USD 2017-09-01 price VHT 118.34 USD 2017-09-01 price GLD 76.22 USD 2017-09-08 price VBMPX 64.85 USD 2017-09-08 price RGAGX 70.11 USD 2017-09-08 price ITOT 185.43 USD 2017-09-08 price VEA 229.59 USD 2017-09-08 price VHT 117.46 USD 2017-09-08 price GLD 76.18 USD ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/contrib/examples/example.beancount0000644000175000001440000115507700000000000021453 0ustar00jakobusers00000000000000;; -*- mode: org; mode: beancount; -*- ;; Birth: 1980-05-12 ;; Dates: 2015-01-01 - 2017-09-12 ;; THIS FILE HAS BEEN AUTO-GENERATED. * Options option "title" "Example Beancount file" option "operating_currency" "USD" * Commodities 1792-01-01 commodity USD export: "CASH" name: "US Dollar" 1900-01-01 commodity VMMXX export: "MUTF:VMMXX (MONEY:USD)" 1980-05-12 commodity VACHR export: "IGNORE" name: "Employer Vacation Hours" 1980-05-12 commodity IRAUSD export: "IGNORE" name: "US 401k and IRA Contributions" 1995-09-18 commodity VBMPX export: "MUTF:VBMPX" name: "Vanguard Total Bond Market Index Fund Institutional Plus Shares" price: "USD:google/MUTF:VBMPX" 2004-01-20 commodity ITOT export: "NYSEARCA:ITOT" name: "iShares Core S&P Total U.S. Stock Market ETF" price: "USD:google/NYSEARCA:ITOT" 2004-01-26 commodity VHT export: "NYSEARCA:VHT" name: "Vanguard Health Care ETF" price: "USD:google/NYSEARCA:VHT" 2004-11-01 commodity GLD export: "NYSEARCA:GLD" name: "SPDR Gold Trust (ETF)" price: "USD:google/NYSEARCA:GLD" 2007-07-20 commodity VEA export: "NYSEARCA:VEA" name: "Vanguard FTSE Developed Markets ETF" price: "USD:google/NYSEARCA:VEA" 2009-05-01 commodity RGAGX export: "MUTF:RGAGX" name: "American Funds The Growth Fund of America Class R-6" price: "USD:google/MUTF:RGAGX" * Equity Accounts 1980-05-12 open Equity:Opening-Balances 1980-05-12 open Liabilities:AccountsPayable * Banking 2015-01-01 open Assets:US:BofA address: "123 America Street, LargeTown, USA" institution: "Bank of America" phone: "+1.012.345.6789" 2015-01-01 open Assets:US:BofA:Checking USD account: "00234-48574897" 2015-01-01 * "Opening Balance for checking account" Assets:US:BofA:Checking 3490.52 USD Equity:Opening-Balances -3490.52 USD 2015-01-02 balance Assets:US:BofA:Checking 4841.12 USD 2015-01-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-01-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-01-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-01-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -82.42 USD Expenses:Home:Phone 82.42 USD 2015-01-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.05 USD Expenses:Home:Internet 80.05 USD 2015-01-30 balance Assets:US:BofA:Checking 4844.07 USD 2015-02-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-02-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-02-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-02-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -53.50 USD Expenses:Home:Phone 53.50 USD 2015-02-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.12 USD Expenses:Home:Internet 80.12 USD 2015-02-24 balance Assets:US:BofA:Checking 2850.71 USD 2015-03-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-03-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-03-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-03-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -48.02 USD Expenses:Home:Phone 48.02 USD 2015-03-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.14 USD Expenses:Home:Internet 80.14 USD 2015-03-25 balance Assets:US:BofA:Checking 2353.30 USD 2015-04-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-04-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-04-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-04-17 balance Assets:US:BofA:Checking 1787.80 USD 2015-04-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -67.68 USD Expenses:Home:Phone 67.68 USD 2015-04-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.01 USD Expenses:Home:Internet 80.01 USD 2015-05-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-05-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-05-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-05-11 balance Assets:US:BofA:Checking 1244.41 USD 2015-05-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -83.72 USD Expenses:Home:Phone 83.72 USD 2015-05-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.06 USD Expenses:Home:Internet 80.06 USD 2015-06-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-06-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-06-07 balance Assets:US:BofA:Checking 1377.83 USD 2015-06-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-06-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -57.18 USD Expenses:Home:Phone 57.18 USD 2015-06-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.10 USD Expenses:Home:Internet 80.10 USD 2015-07-01 balance Assets:US:BofA:Checking 1727.34 USD 2015-07-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-07-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-07-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-07-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -56.96 USD Expenses:Home:Phone 56.96 USD 2015-07-21 balance Assets:US:BofA:Checking 1293.64 USD 2015-07-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.04 USD Expenses:Home:Internet 80.04 USD 2015-08-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-08-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-08-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-08-10 balance Assets:US:BofA:Checking 631.39 USD 2015-08-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -67.40 USD Expenses:Home:Phone 67.40 USD 2015-08-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.95 USD Expenses:Home:Internet 79.95 USD 2015-09-04 balance Assets:US:BofA:Checking 5585.24 USD 2015-09-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-09-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-09-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-09-11 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -4500 USD Assets:US:ETrade:Cash 4500 USD 2015-09-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -52.34 USD Expenses:Home:Phone 52.34 USD 2015-09-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.80 USD Expenses:Home:Internet 79.80 USD 2015-09-28 balance Assets:US:BofA:Checking 2896.88 USD 2015-10-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-10-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-10-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-10-19 balance Assets:US:BofA:Checking 2575.21 USD 2015-10-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -66.34 USD Expenses:Home:Phone 66.34 USD 2015-10-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.95 USD Expenses:Home:Internet 79.95 USD 2015-11-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-11-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-11-06 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -4000 USD Assets:US:ETrade:Cash 4000 USD 2015-11-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-11-16 balance Assets:US:BofA:Checking 466.36 USD 2015-11-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -75.53 USD Expenses:Home:Phone 75.53 USD 2015-11-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.06 USD Expenses:Home:Internet 80.06 USD 2015-12-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-12-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-12-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-12-10 balance Assets:US:BofA:Checking 2498.64 USD 2015-12-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -53.21 USD Expenses:Home:Phone 53.21 USD 2015-12-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.84 USD Expenses:Home:Internet 79.84 USD 2015-12-31 balance Assets:US:BofA:Checking 5197.73 USD 2016-01-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-01-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-01-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-01-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -66.56 USD Expenses:Home:Phone 66.56 USD 2016-01-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.88 USD Expenses:Home:Internet 79.88 USD 2016-01-30 balance Assets:US:BofA:Checking 7420.60 USD 2016-02-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-02-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-02-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-02-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -40.52 USD Expenses:Home:Phone 40.52 USD 2016-02-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.97 USD Expenses:Home:Internet 79.97 USD 2016-02-28 balance Assets:US:BofA:Checking 6903.12 USD 2016-03-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-03-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-03-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-03-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -64.30 USD Expenses:Home:Phone 64.30 USD 2016-03-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.92 USD Expenses:Home:Internet 79.92 USD 2016-03-22 balance Assets:US:BofA:Checking 4880.60 USD 2016-04-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-04-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-04-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-04-14 balance Assets:US:BofA:Checking 4048.16 USD 2016-04-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -72.45 USD Expenses:Home:Phone 72.45 USD 2016-04-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.86 USD Expenses:Home:Internet 79.86 USD 2016-05-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-05-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-05-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-05-11 balance Assets:US:BofA:Checking 3542.52 USD 2016-05-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -45.93 USD Expenses:Home:Phone 45.93 USD 2016-05-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.87 USD Expenses:Home:Internet 79.87 USD 2016-06-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-06-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-06-05 balance Assets:US:BofA:Checking 3713.92 USD 2016-06-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-06-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -73.94 USD Expenses:Home:Phone 73.94 USD 2016-06-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.87 USD Expenses:Home:Internet 79.87 USD 2016-06-25 balance Assets:US:BofA:Checking 4141.04 USD 2016-07-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-07-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-07-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-07-18 balance Assets:US:BofA:Checking 3744.93 USD 2016-07-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -52.05 USD Expenses:Home:Phone 52.05 USD 2016-07-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.98 USD Expenses:Home:Internet 79.98 USD 2016-08-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-08-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-08-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-08-09 balance Assets:US:BofA:Checking 2494.50 USD 2016-08-12 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -4000 USD Assets:US:ETrade:Cash 4000 USD 2016-08-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -52.30 USD Expenses:Home:Phone 52.30 USD 2016-08-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.86 USD Expenses:Home:Internet 79.86 USD 2016-09-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-09-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-09-05 balance Assets:US:BofA:Checking 610.75 USD 2016-09-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-09-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -62.61 USD Expenses:Home:Phone 62.61 USD 2016-09-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.94 USD Expenses:Home:Internet 79.94 USD 2016-09-23 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -4000 USD Assets:US:ETrade:Cash 4000 USD 2016-09-27 balance Assets:US:BofA:Checking 796.72 USD 2016-10-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-10-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-10-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-10-18 balance Assets:US:BofA:Checking 265.64 USD 2016-10-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -61.41 USD Expenses:Home:Phone 61.41 USD 2016-10-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.20 USD Expenses:Home:Internet 80.20 USD 2016-11-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-11-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-11-07 balance Assets:US:BofA:Checking 2821.23 USD 2016-11-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-11-18 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -3000 USD Assets:US:ETrade:Cash 3000 USD 2016-11-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -78.16 USD Expenses:Home:Phone 78.16 USD 2016-11-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.01 USD Expenses:Home:Internet 80.01 USD 2016-12-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2016-12-05 balance Assets:US:BofA:Checking 3971.92 USD 2016-12-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2016-12-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2016-12-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -70.90 USD Expenses:Home:Phone 70.90 USD 2016-12-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.06 USD Expenses:Home:Internet 80.06 USD 2016-12-31 balance Assets:US:BofA:Checking 6377.23 USD 2017-01-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2017-01-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2017-01-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2017-01-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -58.58 USD Expenses:Home:Phone 58.58 USD 2017-01-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.04 USD Expenses:Home:Internet 80.04 USD 2017-01-29 balance Assets:US:BofA:Checking 5884.67 USD 2017-02-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2017-02-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2017-02-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2017-02-19 balance Assets:US:BofA:Checking 4168.23 USD 2017-02-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -86.40 USD Expenses:Home:Phone 86.40 USD 2017-02-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.17 USD Expenses:Home:Internet 80.17 USD 2017-03-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2017-03-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2017-03-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2017-03-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -53.03 USD Expenses:Home:Phone 53.03 USD 2017-03-20 balance Assets:US:BofA:Checking 3549.42 USD 2017-03-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.05 USD Expenses:Home:Internet 80.05 USD 2017-04-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2017-04-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2017-04-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2017-04-16 balance Assets:US:BofA:Checking 2006.25 USD 2017-04-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -66.19 USD Expenses:Home:Phone 66.19 USD 2017-04-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.91 USD Expenses:Home:Internet 79.91 USD 2017-05-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2017-05-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2017-05-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2017-05-13 balance Assets:US:BofA:Checking 1594.87 USD 2017-05-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -56.50 USD Expenses:Home:Phone 56.50 USD 2017-05-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.83 USD Expenses:Home:Internet 79.83 USD 2017-06-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2017-06-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2017-06-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2017-06-11 balance Assets:US:BofA:Checking 1297.97 USD 2017-06-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -71.41 USD Expenses:Home:Phone 71.41 USD 2017-06-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.99 USD Expenses:Home:Internet 79.99 USD 2017-07-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2017-07-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2017-07-06 balance Assets:US:BofA:Checking 1443.77 USD 2017-07-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2017-07-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -52.63 USD Expenses:Home:Phone 52.63 USD 2017-07-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.90 USD Expenses:Home:Internet 79.90 USD 2017-07-28 balance Assets:US:BofA:Checking 3260.41 USD 2017-08-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2017-08-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2017-08-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2017-08-20 balance Assets:US:BofA:Checking 2298.50 USD 2017-08-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -61.56 USD Expenses:Home:Phone 61.56 USD 2017-08-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.08 USD Expenses:Home:Internet 80.08 USD 2017-08-25 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -4500 USD Assets:US:ETrade:Cash 4500 USD 2017-09-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD * Credit-Cards 1980-05-12 open Liabilities:US:Chase:Slate USD 2015-01-04 * "China Garden" "Eating out with Bill" Liabilities:US:Chase:Slate -37.84 USD Expenses:Food:Restaurant 37.84 USD 2015-01-09 * "Uncle Boons" "Eating out with work buddies" Liabilities:US:Chase:Slate -28.94 USD Expenses:Food:Restaurant 28.94 USD 2015-01-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 66.78 USD Assets:US:BofA:Checking -66.78 USD 2015-01-12 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -25.18 USD Expenses:Food:Restaurant 25.18 USD 2015-01-13 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -50.23 USD Expenses:Food:Restaurant 50.23 USD 2015-01-15 * "Jewel of Morroco" "Eating out with Julie" Liabilities:US:Chase:Slate -27.46 USD Expenses:Food:Restaurant 27.46 USD 2015-01-17 * "Uncle Boons" "Eating out with work buddies" Liabilities:US:Chase:Slate -40.32 USD Expenses:Food:Restaurant 40.32 USD 2015-01-18 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -40.71 USD Expenses:Food:Restaurant 40.71 USD 2015-01-18 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -71.90 USD Expenses:Food:Groceries 71.90 USD 2015-01-20 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -39.54 USD Expenses:Food:Restaurant 39.54 USD 2015-01-21 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -11.07 USD Expenses:Food:Restaurant 11.07 USD 2015-01-22 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -39.44 USD Expenses:Food:Restaurant 39.44 USD 2015-01-25 balance Liabilities:US:Chase:Slate -345.85 USD 2015-01-27 * "Jewel of Morroco" "Eating out with Natasha" Liabilities:US:Chase:Slate -46.57 USD Expenses:Food:Restaurant 46.57 USD 2015-01-31 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -30.04 USD Expenses:Food:Restaurant 30.04 USD 2015-02-03 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-02-04 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -56.03 USD Expenses:Food:Groceries 56.03 USD 2015-02-05 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -59.90 USD Expenses:Food:Restaurant 59.90 USD 2015-02-10 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -17.77 USD Expenses:Food:Restaurant 17.77 USD 2015-02-11 * "Cafe Modagor" "Eating out with Bill" Liabilities:US:Chase:Slate -65.18 USD Expenses:Food:Restaurant 65.18 USD 2015-02-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 741.34 USD Assets:US:BofA:Checking -741.34 USD 2015-02-12 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -76.77 USD Expenses:Food:Groceries 76.77 USD 2015-02-14 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -28.07 USD Expenses:Food:Restaurant 28.07 USD 2015-02-16 balance Liabilities:US:Chase:Slate -104.84 USD 2015-02-17 * "Chichipotle" "Eating out with work buddies" Liabilities:US:Chase:Slate -41.33 USD Expenses:Food:Restaurant 41.33 USD 2015-02-22 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -35.96 USD Expenses:Food:Restaurant 35.96 USD 2015-02-23 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -46.08 USD Expenses:Food:Restaurant 46.08 USD 2015-02-27 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -25.75 USD Expenses:Food:Restaurant 25.75 USD 2015-03-01 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -105.27 USD Expenses:Food:Groceries 105.27 USD 2015-03-02 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -35.46 USD Expenses:Food:Restaurant 35.46 USD 2015-03-03 * "Chichipotle" "Eating out with Natasha" Liabilities:US:Chase:Slate -20.31 USD Expenses:Food:Restaurant 20.31 USD 2015-03-03 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-03-07 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -20.32 USD Expenses:Food:Restaurant 20.32 USD 2015-03-09 * "Kin Soy" "Eating out after work" Liabilities:US:Chase:Slate -23.08 USD Expenses:Food:Restaurant 23.08 USD 2015-03-10 balance Liabilities:US:Chase:Slate -578.40 USD 2015-03-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 601.45 USD Assets:US:BofA:Checking -601.45 USD 2015-03-12 * "Uncle Boons" "Eating out with Julie" Liabilities:US:Chase:Slate -23.05 USD Expenses:Food:Restaurant 23.05 USD 2015-03-16 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -74.79 USD Expenses:Food:Restaurant 74.79 USD 2015-03-16 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -66.83 USD Expenses:Food:Groceries 66.83 USD 2015-03-17 * "Uncle Boons" "Eating out with Julie" Liabilities:US:Chase:Slate -32.50 USD Expenses:Food:Restaurant 32.50 USD 2015-03-22 * "Chichipotle" "Eating out with work buddies" Liabilities:US:Chase:Slate -32.37 USD Expenses:Food:Restaurant 32.37 USD 2015-03-23 * "Rose Flower" "Eating out " Liabilities:US:Chase:Slate -32.33 USD Expenses:Food:Restaurant 32.33 USD 2015-03-27 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -19.83 USD Expenses:Food:Restaurant 19.83 USD 2015-03-27 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -95.01 USD Expenses:Food:Groceries 95.01 USD 2015-03-31 balance Liabilities:US:Chase:Slate -353.66 USD 2015-04-01 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -26.12 USD Expenses:Food:Restaurant 26.12 USD 2015-04-02 * "Rose Flower" "Eating out " Liabilities:US:Chase:Slate -33.54 USD Expenses:Food:Restaurant 33.54 USD 2015-04-03 * "Chichipotle" "Eating out after work" Liabilities:US:Chase:Slate -36.75 USD Expenses:Food:Restaurant 36.75 USD 2015-04-03 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -55.31 USD Expenses:Food:Groceries 55.31 USD 2015-04-03 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-04-06 * "Jewel of Morroco" "Eating out with Natasha" Liabilities:US:Chase:Slate -23.57 USD Expenses:Food:Restaurant 23.57 USD 2015-04-08 * "Kin Soy" "Eating out with work buddies" Liabilities:US:Chase:Slate -25.48 USD Expenses:Food:Restaurant 25.48 USD 2015-04-08 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -103.35 USD Expenses:Food:Groceries 103.35 USD 2015-04-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 797.70 USD Assets:US:BofA:Checking -797.70 USD 2015-04-12 * "Kin Soy" "Eating out with work buddies" Liabilities:US:Chase:Slate -19.92 USD Expenses:Food:Restaurant 19.92 USD 2015-04-16 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -48.08 USD Expenses:Food:Restaurant 48.08 USD 2015-04-19 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -32.42 USD Expenses:Food:Restaurant 32.42 USD 2015-04-20 * "Uncle Boons" "Eating out with Bill" Liabilities:US:Chase:Slate -24.03 USD Expenses:Food:Restaurant 24.03 USD 2015-04-25 * "Uncle Boons" "Eating out after work" Liabilities:US:Chase:Slate -34.31 USD Expenses:Food:Restaurant 34.31 USD 2015-04-27 balance Liabilities:US:Chase:Slate -138.84 USD 2015-04-27 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -96.21 USD Expenses:Food:Groceries 96.21 USD 2015-04-28 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -39.06 USD Expenses:Food:Restaurant 39.06 USD 2015-05-01 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-05-02 * "Cafe Modagor" "Eating out with Bill" Liabilities:US:Chase:Slate -65.86 USD Expenses:Food:Restaurant 65.86 USD 2015-05-03 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -40.25 USD Expenses:Food:Restaurant 40.25 USD 2015-05-04 * "Chichipotle" "Eating out with work buddies" Liabilities:US:Chase:Slate -31.33 USD Expenses:Food:Restaurant 31.33 USD 2015-05-07 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -78.38 USD Expenses:Food:Restaurant 78.38 USD 2015-05-08 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 627.90 USD Assets:US:BofA:Checking -627.90 USD 2015-05-09 * "Kin Soy" "Eating out after work" Liabilities:US:Chase:Slate -17.97 USD Expenses:Food:Restaurant 17.97 USD 2015-05-10 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -35.13 USD Expenses:Food:Restaurant 35.13 USD 2015-05-12 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -98.50 USD Expenses:Food:Groceries 98.50 USD 2015-05-13 * "Jewel of Morroco" "Eating out with work buddies" Liabilities:US:Chase:Slate -30.14 USD Expenses:Food:Restaurant 30.14 USD 2015-05-18 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -35.59 USD Expenses:Food:Restaurant 35.59 USD 2015-05-19 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -75.48 USD Expenses:Food:Groceries 75.48 USD 2015-05-21 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -22.33 USD Expenses:Food:Restaurant 22.33 USD 2015-05-22 * "Chichipotle" "Eating out after work" Liabilities:US:Chase:Slate -44.73 USD Expenses:Food:Restaurant 44.73 USD 2015-05-26 balance Liabilities:US:Chase:Slate -341.90 USD 2015-05-26 * "Jewel of Morroco" "Eating out with Julie" Liabilities:US:Chase:Slate -19.27 USD Expenses:Food:Restaurant 19.27 USD 2015-05-29 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-05-30 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -86.61 USD Expenses:Food:Groceries 86.61 USD 2015-05-31 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -57.82 USD Expenses:Food:Restaurant 57.82 USD 2015-06-01 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -27.64 USD Expenses:Food:Restaurant 27.64 USD 2015-06-03 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -26.98 USD Expenses:Food:Restaurant 26.98 USD 2015-06-07 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -57.74 USD Expenses:Food:Groceries 57.74 USD 2015-06-08 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -35.74 USD Expenses:Food:Restaurant 35.74 USD 2015-06-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 798.81 USD Assets:US:BofA:Checking -798.81 USD 2015-06-11 * "Rose Flower" "Eating out after work" Liabilities:US:Chase:Slate -25.11 USD Expenses:Food:Restaurant 25.11 USD 2015-06-15 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -37.67 USD Expenses:Food:Restaurant 37.67 USD 2015-06-16 balance Liabilities:US:Chase:Slate -37.67 USD 2015-06-16 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -31.21 USD Expenses:Food:Restaurant 31.21 USD 2015-06-18 * "Jewel of Morroco" "Eating out with Natasha" Liabilities:US:Chase:Slate -22.98 USD Expenses:Food:Restaurant 22.98 USD 2015-06-23 * "Goba Goba" "Eating out " Liabilities:US:Chase:Slate -32.07 USD Expenses:Food:Restaurant 32.07 USD 2015-06-24 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -36.49 USD Expenses:Food:Restaurant 36.49 USD 2015-06-26 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -32.92 USD Expenses:Food:Restaurant 32.92 USD 2015-06-26 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -113.34 USD Expenses:Food:Groceries 113.34 USD 2015-07-01 * "China Garden" "Eating out with Julie" Liabilities:US:Chase:Slate -35.65 USD Expenses:Food:Restaurant 35.65 USD 2015-07-01 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-07-05 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -32.08 USD Expenses:Food:Restaurant 32.08 USD 2015-07-05 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -59.73 USD Expenses:Food:Groceries 59.73 USD 2015-07-06 * "Uncle Boons" "Eating out with Julie" Liabilities:US:Chase:Slate -14.85 USD Expenses:Food:Restaurant 14.85 USD 2015-07-09 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -17.81 USD Expenses:Food:Restaurant 17.81 USD 2015-07-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 608.94 USD Assets:US:BofA:Checking -608.94 USD 2015-07-12 balance Liabilities:US:Chase:Slate 22.14 USD 2015-07-12 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -22.14 USD Expenses:Food:Restaurant 22.14 USD 2015-07-17 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -29.57 USD Expenses:Food:Restaurant 29.57 USD 2015-07-19 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -69.06 USD Expenses:Food:Groceries 69.06 USD 2015-07-20 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -19.78 USD Expenses:Food:Restaurant 19.78 USD 2015-07-22 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -24.58 USD Expenses:Food:Restaurant 24.58 USD 2015-07-23 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -32.48 USD Expenses:Food:Restaurant 32.48 USD 2015-07-24 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -82.77 USD Expenses:Food:Groceries 82.77 USD 2015-07-28 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -48.49 USD Expenses:Food:Restaurant 48.49 USD 2015-07-29 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -40.66 USD Expenses:Food:Restaurant 40.66 USD 2015-07-29 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -65.57 USD Expenses:Food:Groceries 65.57 USD 2015-07-29 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-07-31 * "Uncle Boons" "Eating out with Bill" Liabilities:US:Chase:Slate -21.91 USD Expenses:Food:Restaurant 21.91 USD 2015-08-01 * "Uncle Boons" "Eating out with work buddies" Liabilities:US:Chase:Slate -32.20 USD Expenses:Food:Restaurant 32.20 USD 2015-08-04 balance Liabilities:US:Chase:Slate -587.07 USD 2015-08-06 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -32.68 USD Expenses:Food:Restaurant 32.68 USD 2015-08-07 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -44.06 USD Expenses:Food:Groceries 44.06 USD 2015-08-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 663.81 USD Assets:US:BofA:Checking -663.81 USD 2015-08-08 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -41.97 USD Expenses:Food:Restaurant 41.97 USD 2015-08-12 * "Cafe Modagor" "Eating out with Bill" Liabilities:US:Chase:Slate -66.14 USD Expenses:Food:Restaurant 66.14 USD 2015-08-14 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -38.45 USD Expenses:Food:Restaurant 38.45 USD 2015-08-17 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -69.87 USD Expenses:Food:Groceries 69.87 USD 2015-08-19 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -28.47 USD Expenses:Food:Restaurant 28.47 USD 2015-08-20 * "Goba Goba" "Eating out alone" Liabilities:US:Chase:Slate -30.87 USD Expenses:Food:Restaurant 30.87 USD 2015-08-24 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -35.06 USD Expenses:Food:Restaurant 35.06 USD 2015-08-27 balance Liabilities:US:Chase:Slate -310.83 USD 2015-08-27 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -72.58 USD Expenses:Food:Groceries 72.58 USD 2015-08-29 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -49.67 USD Expenses:Food:Restaurant 49.67 USD 2015-08-29 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-08-30 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -44.87 USD Expenses:Food:Restaurant 44.87 USD 2015-09-01 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -26.32 USD Expenses:Food:Restaurant 26.32 USD 2015-09-03 * "Jewel of Morroco" "Eating out with work buddies" Liabilities:US:Chase:Slate -23.14 USD Expenses:Food:Restaurant 23.14 USD 2015-09-07 event "location" "Los Angeles" 2015-09-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 688.42 USD Assets:US:BofA:Checking -688.42 USD 2015-09-08 * "Mr. Marcel" "" #trip-los-angeles-2015 Liabilities:US:Chase:Slate -37.30 USD Expenses:Food:Restaurant 37.30 USD 2015-09-08 * "Starbucks" "" #trip-los-angeles-2015 Liabilities:US:Chase:Slate -6.12 USD Expenses:Food:Coffee 6.12 USD 2015-09-09 * "Dupar's" "" #trip-los-angeles-2015 Liabilities:US:Chase:Slate -20.89 USD Expenses:Food:Restaurant 20.89 USD 2015-09-09 * "Chipotle" "" #trip-los-angeles-2015 Liabilities:US:Chase:Slate -16.08 USD Expenses:Food:Restaurant 16.08 USD 2015-09-09 * "Starbucks" "" #trip-los-angeles-2015 Liabilities:US:Chase:Slate -4.86 USD Expenses:Food:Coffee 4.86 USD 2015-09-10 * "Mr. Marcel" "" #trip-los-angeles-2015 Liabilities:US:Chase:Slate -44.67 USD Expenses:Food:Restaurant 44.67 USD 2015-09-10 * "Dupar's" "" #trip-los-angeles-2015 Liabilities:US:Chase:Slate -26.44 USD Expenses:Food:Restaurant 26.44 USD 2015-09-10 * "Starbucks" "" #trip-los-angeles-2015 Liabilities:US:Chase:Slate -5.27 USD Expenses:Food:Coffee 5.27 USD 2015-09-12 * "Mr. Marcel" "" #trip-los-angeles-2015 Liabilities:US:Chase:Slate -43.08 USD Expenses:Food:Restaurant 43.08 USD 2015-09-12 * "Banana Leaf" "" #trip-los-angeles-2015 Liabilities:US:Chase:Slate -27.51 USD Expenses:Food:Restaurant 27.51 USD 2015-09-12 * "Pampas Grill" "" #trip-los-angeles-2015 Liabilities:US:Chase:Slate -27.05 USD Expenses:Food:Restaurant 27.05 USD 2015-09-13 * "Mr. Marcel" "" #trip-los-angeles-2015 Liabilities:US:Chase:Slate -22.80 USD Expenses:Food:Restaurant 22.80 USD 2015-09-14 * "Starbucks" "" #trip-los-angeles-2015 Liabilities:US:Chase:Slate -5.68 USD Expenses:Food:Coffee 5.68 USD 2015-09-15 * "Banana Leaf" "" #trip-los-angeles-2015 Liabilities:US:Chase:Slate -25.28 USD Expenses:Food:Restaurant 25.28 USD 2015-09-15 * "Pampas Grill" "" #trip-los-angeles-2015 Liabilities:US:Chase:Slate -24.12 USD Expenses:Food:Restaurant 24.12 USD 2015-09-16 * "China Garden" "Eating out with Natasha" Liabilities:US:Chase:Slate -41.01 USD Expenses:Food:Restaurant 41.01 USD 2015-09-16 * "Banana Leaf" "" #trip-los-angeles-2015 Liabilities:US:Chase:Slate -23.03 USD Expenses:Food:Restaurant 23.03 USD 2015-09-16 * "Chipotle" "" #trip-los-angeles-2015 Liabilities:US:Chase:Slate -17.88 USD Expenses:Food:Restaurant 17.88 USD 2015-09-16 * "Consume vacation days" Assets:US:Hoogle:Vacation -72 VACHR Expenses:Vacation 72 VACHR 2015-09-16 event "location" "New Metropolis" 2015-09-19 * "China Garden" "Eating out with Natasha" Liabilities:US:Chase:Slate -32.34 USD Expenses:Food:Restaurant 32.34 USD 2015-09-24 * "China Garden" "Eating out " Liabilities:US:Chase:Slate -19.85 USD Expenses:Food:Restaurant 19.85 USD 2015-09-25 balance Liabilities:US:Chase:Slate -430.25 USD 2015-09-27 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -64.95 USD Expenses:Food:Groceries 64.95 USD 2015-09-27 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-09-29 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -28.34 USD Expenses:Food:Restaurant 28.34 USD 2015-10-02 * "Cafe Modagor" "Eating out with Julie" Liabilities:US:Chase:Slate -31.89 USD Expenses:Food:Restaurant 31.89 USD 2015-10-03 * "China Garden" "Eating out with Natasha" Liabilities:US:Chase:Slate -39.17 USD Expenses:Food:Restaurant 39.17 USD 2015-10-07 * "Jewel of Morroco" "Eating out with Julie" Liabilities:US:Chase:Slate -19.37 USD Expenses:Food:Restaurant 19.37 USD 2015-10-08 * "Rose Flower" "Eating out after work" Liabilities:US:Chase:Slate -27.19 USD Expenses:Food:Restaurant 27.19 USD 2015-10-09 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -20.17 USD Expenses:Food:Restaurant 20.17 USD 2015-10-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 403.27 USD Assets:US:BofA:Checking -403.27 USD 2015-10-11 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -50.41 USD Expenses:Food:Restaurant 50.41 USD 2015-10-13 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -54.93 USD Expenses:Food:Groceries 54.93 USD 2015-10-14 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -35.25 USD Expenses:Food:Restaurant 35.25 USD 2015-10-15 balance Liabilities:US:Chase:Slate -518.65 USD 2015-10-17 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -31.12 USD Expenses:Food:Restaurant 31.12 USD 2015-10-22 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -40.46 USD Expenses:Food:Restaurant 40.46 USD 2015-10-24 * "China Garden" "Eating out with work buddies" Liabilities:US:Chase:Slate -33.23 USD Expenses:Food:Restaurant 33.23 USD 2015-10-26 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-10-29 * "China Garden" "Eating out with Natasha" Liabilities:US:Chase:Slate -19.22 USD Expenses:Food:Restaurant 19.22 USD 2015-11-01 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -39.78 USD Expenses:Food:Restaurant 39.78 USD 2015-11-02 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -81.97 USD Expenses:Food:Groceries 81.97 USD 2015-11-06 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -42.46 USD Expenses:Food:Restaurant 42.46 USD 2015-11-07 balance Liabilities:US:Chase:Slate -926.89 USD 2015-11-11 * "Cafe Modagor" "Eating out with Julie" Liabilities:US:Chase:Slate -45.93 USD Expenses:Food:Restaurant 45.93 USD 2015-11-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 594.76 USD Assets:US:BofA:Checking -594.76 USD 2015-11-14 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -24.93 USD Expenses:Food:Restaurant 24.93 USD 2015-11-16 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -43.89 USD Expenses:Food:Restaurant 43.89 USD 2015-11-17 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -24.07 USD Expenses:Food:Restaurant 24.07 USD 2015-11-19 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -23.74 USD Expenses:Food:Restaurant 23.74 USD 2015-11-19 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -76.94 USD Expenses:Food:Groceries 76.94 USD 2015-11-24 * "Goba Goba" "Eating out " Liabilities:US:Chase:Slate -36.01 USD Expenses:Food:Restaurant 36.01 USD 2015-11-25 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -52.31 USD Expenses:Food:Restaurant 52.31 USD 2015-11-25 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-11-30 * "Chichipotle" "Eating out with Natasha" Liabilities:US:Chase:Slate -23.39 USD Expenses:Food:Restaurant 23.39 USD 2015-12-03 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -28.32 USD Expenses:Food:Restaurant 28.32 USD 2015-12-04 balance Liabilities:US:Chase:Slate -831.66 USD 2015-12-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 482.79 USD Assets:US:BofA:Checking -482.79 USD 2015-12-08 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -29.19 USD Expenses:Food:Restaurant 29.19 USD 2015-12-08 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -99.86 USD Expenses:Food:Groceries 99.86 USD 2015-12-11 * "China Garden" "Eating out with Bill" Liabilities:US:Chase:Slate -40.46 USD Expenses:Food:Restaurant 40.46 USD 2015-12-14 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -32.79 USD Expenses:Food:Restaurant 32.79 USD 2015-12-18 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -26.60 USD Expenses:Food:Restaurant 26.60 USD 2015-12-19 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -31.58 USD Expenses:Food:Restaurant 31.58 USD 2015-12-21 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -31.35 USD Expenses:Food:Restaurant 31.35 USD 2015-12-22 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -45.56 USD Expenses:Food:Restaurant 45.56 USD 2015-12-22 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-12-25 * "Uncle Boons" "Eating out with work buddies" Liabilities:US:Chase:Slate -36.76 USD Expenses:Food:Restaurant 36.76 USD 2015-12-27 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -42.53 USD Expenses:Food:Restaurant 42.53 USD 2015-12-27 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -53.36 USD Expenses:Food:Groceries 53.36 USD 2015-12-29 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -39.79 USD Expenses:Food:Restaurant 39.79 USD 2015-12-30 balance Liabilities:US:Chase:Slate -978.70 USD 2016-01-03 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -58.01 USD Expenses:Food:Restaurant 58.01 USD 2016-01-06 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -11.01 USD Expenses:Food:Restaurant 11.01 USD 2016-01-08 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -25.37 USD Expenses:Food:Restaurant 25.37 USD 2016-01-08 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 695.03 USD Assets:US:BofA:Checking -695.03 USD 2016-01-10 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -85.69 USD Expenses:Food:Groceries 85.69 USD 2016-01-13 * "Chichipotle" "Eating out after work" Liabilities:US:Chase:Slate -42.59 USD Expenses:Food:Restaurant 42.59 USD 2016-01-18 * "China Garden" "Eating out with work buddies" Liabilities:US:Chase:Slate -26.11 USD Expenses:Food:Restaurant 26.11 USD 2016-01-19 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -58.59 USD Expenses:Food:Groceries 58.59 USD 2016-01-22 * "China Garden" "Eating out with Bill" Liabilities:US:Chase:Slate -34.62 USD Expenses:Food:Restaurant 34.62 USD 2016-01-22 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2016-01-26 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -69.95 USD Expenses:Food:Groceries 69.95 USD 2016-01-27 * "Cafe Modagor" "Eating out with Julie" Liabilities:US:Chase:Slate -20.38 USD Expenses:Food:Restaurant 20.38 USD 2016-01-28 balance Liabilities:US:Chase:Slate -835.99 USD 2016-01-29 * "China Garden" "Eating out with Natasha" Liabilities:US:Chase:Slate -31.99 USD Expenses:Food:Restaurant 31.99 USD 2016-01-30 * "Kin Soy" "Eating out with Julie" Liabilities:US:Chase:Slate -28.29 USD Expenses:Food:Restaurant 28.29 USD 2016-01-31 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -22.45 USD Expenses:Food:Restaurant 22.45 USD 2016-02-01 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -32.99 USD Expenses:Food:Restaurant 32.99 USD 2016-02-04 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -33.50 USD Expenses:Food:Restaurant 33.50 USD 2016-02-09 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -22.04 USD Expenses:Food:Restaurant 22.04 USD 2016-02-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 629.19 USD Assets:US:BofA:Checking -629.19 USD 2016-02-11 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -102.42 USD Expenses:Food:Groceries 102.42 USD 2016-02-12 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -22.73 USD Expenses:Food:Restaurant 22.73 USD 2016-02-15 * "China Garden" "Eating out with Bill" Liabilities:US:Chase:Slate -30.47 USD Expenses:Food:Restaurant 30.47 USD 2016-02-16 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -22.88 USD Expenses:Food:Restaurant 22.88 USD 2016-02-17 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -36.41 USD Expenses:Food:Restaurant 36.41 USD 2016-02-19 balance Liabilities:US:Chase:Slate -592.97 USD 2016-02-22 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -54.22 USD Expenses:Food:Restaurant 54.22 USD 2016-02-22 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -76.58 USD Expenses:Food:Groceries 76.58 USD 2016-02-24 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2016-02-25 * "China Garden" "Eating out " Liabilities:US:Chase:Slate -51.79 USD Expenses:Food:Restaurant 51.79 USD 2016-02-26 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -31.79 USD Expenses:Food:Restaurant 31.79 USD 2016-02-27 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -18.01 USD Expenses:Food:Restaurant 18.01 USD 2016-03-01 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -37.90 USD Expenses:Food:Restaurant 37.90 USD 2016-03-05 * "Jewel of Morroco" "Eating out with work buddies" Liabilities:US:Chase:Slate -20.46 USD Expenses:Food:Restaurant 20.46 USD 2016-03-06 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -77.97 USD Expenses:Food:Groceries 77.97 USD 2016-03-07 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -19.59 USD Expenses:Food:Restaurant 19.59 USD 2016-03-08 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 759.90 USD Assets:US:BofA:Checking -759.90 USD 2016-03-12 * "China Garden" "Eating out with Julie" Liabilities:US:Chase:Slate -36.68 USD Expenses:Food:Restaurant 36.68 USD 2016-03-13 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -17.71 USD Expenses:Food:Restaurant 17.71 USD 2016-03-16 balance Liabilities:US:Chase:Slate -395.77 USD 2016-03-18 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -30.42 USD Expenses:Food:Restaurant 30.42 USD 2016-03-20 * "China Garden" "Eating out " Liabilities:US:Chase:Slate -34.98 USD Expenses:Food:Restaurant 34.98 USD 2016-03-23 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -61.40 USD Expenses:Food:Groceries 61.40 USD 2016-03-25 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -17.61 USD Expenses:Food:Restaurant 17.61 USD 2016-03-27 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2016-03-30 * "Kin Soy" "Eating out with work buddies" Liabilities:US:Chase:Slate -22.59 USD Expenses:Food:Restaurant 22.59 USD 2016-04-03 * "Rose Flower" "Eating out with work buddies" Liabilities:US:Chase:Slate -37.41 USD Expenses:Food:Restaurant 37.41 USD 2016-04-03 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -83.94 USD Expenses:Food:Groceries 83.94 USD 2016-04-07 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -21.74 USD Expenses:Food:Restaurant 21.74 USD 2016-04-08 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 494.49 USD Assets:US:BofA:Checking -494.49 USD 2016-04-09 balance Liabilities:US:Chase:Slate -331.37 USD 2016-04-11 event "location" "Los Angeles" 2016-04-12 * "Banana Leaf" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -24.65 USD Expenses:Food:Restaurant 24.65 USD 2016-04-12 * "Dupar's" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -27.13 USD Expenses:Food:Restaurant 27.13 USD 2016-04-12 * "Starbucks" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -5.94 USD Expenses:Food:Coffee 5.94 USD 2016-04-13 * "Banana Leaf" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -21.93 USD Expenses:Food:Restaurant 21.93 USD 2016-04-13 * "Dupar's" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -32.01 USD Expenses:Food:Restaurant 32.01 USD 2016-04-13 * "E.B.'s Beer and Wine" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -8.14 USD Expenses:Food:Alcohol 8.14 USD 2016-04-14 * "Dupar's" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -25.88 USD Expenses:Food:Restaurant 25.88 USD 2016-04-15 * "Mr. Marcel" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -43.66 USD Expenses:Food:Restaurant 43.66 USD 2016-04-15 * "Banana Leaf" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -23.85 USD Expenses:Food:Restaurant 23.85 USD 2016-04-15 * "Pampas Grill" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -28.16 USD Expenses:Food:Restaurant 28.16 USD 2016-04-15 * "E.B.'s Beer and Wine" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -9.42 USD Expenses:Food:Alcohol 9.42 USD 2016-04-16 * "Dupar's" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -31.02 USD Expenses:Food:Restaurant 31.02 USD 2016-04-16 * "E.B.'s Beer and Wine" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -8.57 USD Expenses:Food:Alcohol 8.57 USD 2016-04-17 * "Banana Leaf" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -30.91 USD Expenses:Food:Restaurant 30.91 USD 2016-04-17 * "Chipotle" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -17.90 USD Expenses:Food:Restaurant 17.90 USD 2016-04-18 * "Chipotle" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -16.29 USD Expenses:Food:Restaurant 16.29 USD 2016-04-18 * "E.B.'s Beer and Wine" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -8.76 USD Expenses:Food:Alcohol 8.76 USD 2016-04-19 * "Mr. Marcel" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -32.01 USD Expenses:Food:Restaurant 32.01 USD 2016-04-19 * "Banana Leaf" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -24.37 USD Expenses:Food:Restaurant 24.37 USD 2016-04-19 * "Starbucks" "" #trip-los-angeles-2016 Liabilities:US:Chase:Slate -5.23 USD Expenses:Food:Coffee 5.23 USD 2016-04-19 * "Consume vacation days" Assets:US:Hoogle:Vacation -64 VACHR Expenses:Vacation 64 VACHR 2016-04-19 event "location" "New Metropolis" 2016-04-20 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -46.69 USD Expenses:Food:Restaurant 46.69 USD 2016-04-21 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -18.25 USD Expenses:Food:Restaurant 18.25 USD 2016-04-22 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -54.13 USD Expenses:Food:Restaurant 54.13 USD 2016-04-23 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2016-04-25 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -17.43 USD Expenses:Food:Restaurant 17.43 USD 2016-04-25 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -64.35 USD Expenses:Food:Groceries 64.35 USD 2016-04-29 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -53.91 USD Expenses:Food:Restaurant 53.91 USD 2016-05-01 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -60.48 USD Expenses:Food:Restaurant 60.48 USD 2016-05-04 balance Liabilities:US:Chase:Slate -1192.44 USD 2016-05-05 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -43.14 USD Expenses:Food:Restaurant 43.14 USD 2016-05-05 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -95.03 USD Expenses:Food:Groceries 95.03 USD 2016-05-08 * "Chichipotle" "Eating out with Natasha" Liabilities:US:Chase:Slate -29.13 USD Expenses:Food:Restaurant 29.13 USD 2016-05-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 585.53 USD Assets:US:BofA:Checking -585.53 USD 2016-05-10 * "Cafe Modagor" "Eating out with Julie" Liabilities:US:Chase:Slate -29.68 USD Expenses:Food:Restaurant 29.68 USD 2016-05-14 * "Cafe Modagor" "Eating out with Julie" Liabilities:US:Chase:Slate -30.77 USD Expenses:Food:Restaurant 30.77 USD 2016-05-15 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -38.18 USD Expenses:Food:Restaurant 38.18 USD 2016-05-20 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -28.11 USD Expenses:Food:Restaurant 28.11 USD 2016-05-21 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -39.60 USD Expenses:Food:Restaurant 39.60 USD 2016-05-21 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -90.17 USD Expenses:Food:Groceries 90.17 USD 2016-05-23 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2016-05-26 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -37.61 USD Expenses:Food:Restaurant 37.61 USD 2016-05-27 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -26.33 USD Expenses:Food:Restaurant 26.33 USD 2016-05-30 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -32.27 USD Expenses:Food:Restaurant 32.27 USD 2016-05-31 balance Liabilities:US:Chase:Slate -1246.93 USD 2016-06-02 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -68.13 USD Expenses:Food:Groceries 68.13 USD 2016-06-03 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -46.57 USD Expenses:Food:Restaurant 46.57 USD 2016-06-04 * "China Garden" "Eating out with Natasha" Liabilities:US:Chase:Slate -21.83 USD Expenses:Food:Restaurant 21.83 USD 2016-06-06 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -14.17 USD Expenses:Food:Restaurant 14.17 USD 2016-06-08 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -42.18 USD Expenses:Food:Restaurant 42.18 USD 2016-06-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 704.67 USD Assets:US:BofA:Checking -704.67 USD 2016-06-11 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -68.75 USD Expenses:Food:Restaurant 68.75 USD 2016-06-15 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -31.02 USD Expenses:Food:Restaurant 31.02 USD 2016-06-18 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -37.36 USD Expenses:Food:Restaurant 37.36 USD 2016-06-18 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -55.88 USD Expenses:Food:Groceries 55.88 USD 2016-06-21 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -36.89 USD Expenses:Food:Restaurant 36.89 USD 2016-06-23 * "Kin Soy" "Eating out with work buddies" Liabilities:US:Chase:Slate -35.14 USD Expenses:Food:Restaurant 35.14 USD 2016-06-25 * "Kin Soy" "Eating out with Julie" Liabilities:US:Chase:Slate -36.95 USD Expenses:Food:Restaurant 36.95 USD 2016-06-25 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2016-06-28 balance Liabilities:US:Chase:Slate -1157.13 USD 2016-06-30 * "Kin Soy" "Eating out with work buddies" Liabilities:US:Chase:Slate -21.76 USD Expenses:Food:Restaurant 21.76 USD 2016-07-01 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -88.22 USD Expenses:Food:Groceries 88.22 USD 2016-07-03 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -18.87 USD Expenses:Food:Restaurant 18.87 USD 2016-07-05 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -25.31 USD Expenses:Food:Restaurant 25.31 USD 2016-07-09 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -17.49 USD Expenses:Food:Restaurant 17.49 USD 2016-07-10 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -78.12 USD Expenses:Food:Groceries 78.12 USD 2016-07-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 628.31 USD Assets:US:BofA:Checking -628.31 USD 2016-07-14 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -25.30 USD Expenses:Food:Restaurant 25.30 USD 2016-07-17 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -27.23 USD Expenses:Food:Restaurant 27.23 USD 2016-07-18 * "Rose Flower" "Eating out after work" Liabilities:US:Chase:Slate -17.34 USD Expenses:Food:Restaurant 17.34 USD 2016-07-21 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -40.29 USD Expenses:Food:Restaurant 40.29 USD 2016-07-23 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -21.86 USD Expenses:Food:Restaurant 21.86 USD 2016-07-23 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2016-07-25 balance Liabilities:US:Chase:Slate -1030.61 USD 2016-07-25 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -75.61 USD Expenses:Food:Groceries 75.61 USD 2016-07-26 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -28.43 USD Expenses:Food:Restaurant 28.43 USD 2016-07-28 * "Uncle Boons" "Eating out " Liabilities:US:Chase:Slate -11.07 USD Expenses:Food:Restaurant 11.07 USD 2016-07-29 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -33.20 USD Expenses:Food:Restaurant 33.20 USD 2016-08-02 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -27.76 USD Expenses:Food:Restaurant 27.76 USD 2016-08-06 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -29.05 USD Expenses:Food:Restaurant 29.05 USD 2016-08-11 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -16.95 USD Expenses:Food:Restaurant 16.95 USD 2016-08-11 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -118.95 USD Expenses:Food:Groceries 118.95 USD 2016-08-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 448.79 USD Assets:US:BofA:Checking -448.79 USD 2016-08-12 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -22.07 USD Expenses:Food:Restaurant 22.07 USD 2016-08-17 balance Liabilities:US:Chase:Slate -944.91 USD 2016-08-17 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -24.43 USD Expenses:Food:Restaurant 24.43 USD 2016-08-20 * "Jewel of Morroco" "Eating out with Natasha" Liabilities:US:Chase:Slate -44.63 USD Expenses:Food:Restaurant 44.63 USD 2016-08-21 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2016-08-23 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -31.35 USD Expenses:Food:Restaurant 31.35 USD 2016-08-25 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -97.97 USD Expenses:Food:Groceries 97.97 USD 2016-08-26 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -19.73 USD Expenses:Food:Restaurant 19.73 USD 2016-08-27 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -26.48 USD Expenses:Food:Restaurant 26.48 USD 2016-08-28 * "Chichipotle" "Eating out after work" Liabilities:US:Chase:Slate -30.66 USD Expenses:Food:Restaurant 30.66 USD 2016-09-01 * "China Garden" "Eating out with work buddies" Liabilities:US:Chase:Slate -49.56 USD Expenses:Food:Restaurant 49.56 USD 2016-09-06 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -28.16 USD Expenses:Food:Restaurant 28.16 USD 2016-09-07 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -15.41 USD Expenses:Food:Restaurant 15.41 USD 2016-09-09 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -49.40 USD Expenses:Food:Groceries 49.40 USD 2016-09-11 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -28.88 USD Expenses:Food:Restaurant 28.88 USD 2016-09-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 707.68 USD Assets:US:BofA:Checking -707.68 USD 2016-09-15 balance Liabilities:US:Chase:Slate -803.89 USD 2016-09-15 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -25.63 USD Expenses:Food:Restaurant 25.63 USD 2016-09-18 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -83.46 USD Expenses:Food:Groceries 83.46 USD 2016-09-18 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2016-09-20 * "Kin Soy" "Eating out after work" Liabilities:US:Chase:Slate -54.05 USD Expenses:Food:Restaurant 54.05 USD 2016-09-21 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -20.53 USD Expenses:Food:Restaurant 20.53 USD 2016-09-25 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -35.62 USD Expenses:Food:Restaurant 35.62 USD 2016-09-25 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -75.76 USD Expenses:Food:Groceries 75.76 USD 2016-09-26 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -23.39 USD Expenses:Food:Restaurant 23.39 USD 2016-09-28 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -14.74 USD Expenses:Food:Restaurant 14.74 USD 2016-09-29 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -42.59 USD Expenses:Food:Restaurant 42.59 USD 2016-10-04 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -42.36 USD Expenses:Food:Restaurant 42.36 USD 2016-10-05 balance Liabilities:US:Chase:Slate -1342.02 USD 2016-10-08 * "Cafe Modagor" "Eating out with Julie" Liabilities:US:Chase:Slate -23.79 USD Expenses:Food:Restaurant 23.79 USD 2016-10-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 612.68 USD Assets:US:BofA:Checking -612.68 USD 2016-10-12 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -50.76 USD Expenses:Food:Groceries 50.76 USD 2016-10-13 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -17.13 USD Expenses:Food:Restaurant 17.13 USD 2016-10-15 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -45.34 USD Expenses:Food:Restaurant 45.34 USD 2016-10-17 * "Rose Flower" "Eating out after work" Liabilities:US:Chase:Slate -32.38 USD Expenses:Food:Restaurant 32.38 USD 2016-10-18 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -40.41 USD Expenses:Food:Restaurant 40.41 USD 2016-10-18 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -94.78 USD Expenses:Food:Groceries 94.78 USD 2016-10-19 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2016-10-22 * "Rose Flower" "Eating out " Liabilities:US:Chase:Slate -44.32 USD Expenses:Food:Restaurant 44.32 USD 2016-10-25 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -67.50 USD Expenses:Food:Groceries 67.50 USD 2016-10-26 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -23.74 USD Expenses:Food:Restaurant 23.74 USD 2016-10-29 balance Liabilities:US:Chase:Slate -1289.49 USD 2016-10-31 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -17.84 USD Expenses:Food:Restaurant 17.84 USD 2016-11-02 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -111.98 USD Expenses:Food:Groceries 111.98 USD 2016-11-04 * "Goba Goba" "Eating out alone" Liabilities:US:Chase:Slate -33.05 USD Expenses:Food:Restaurant 33.05 USD 2016-11-05 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -15.35 USD Expenses:Food:Restaurant 15.35 USD 2016-11-07 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -22.77 USD Expenses:Food:Restaurant 22.77 USD 2016-11-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 723.34 USD Assets:US:BofA:Checking -723.34 USD 2016-11-12 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -36.75 USD Expenses:Food:Restaurant 36.75 USD 2016-11-14 * "Jewel of Morroco" "Eating out with Natasha" Liabilities:US:Chase:Slate -21.36 USD Expenses:Food:Restaurant 21.36 USD 2016-11-16 event "location" "Chicago" 2016-11-18 * "Mercadito" "" #trip-chicago-2016 Liabilities:US:Chase:Slate -39.66 USD Expenses:Food:Restaurant 39.66 USD 2016-11-18 * "25 Degrees Burger Bar" "" #trip-chicago-2016 Liabilities:US:Chase:Slate -18.22 USD Expenses:Food:Restaurant 18.22 USD 2016-11-18 * "Eataly Chicago" "" #trip-chicago-2016 Liabilities:US:Chase:Slate -33.75 USD Expenses:Food:Restaurant 33.75 USD 2016-11-18 * "Argo Tea" "" #trip-chicago-2016 Liabilities:US:Chase:Slate -6.15 USD Expenses:Food:Coffee 6.15 USD 2016-11-19 * "Star of Siam" "" #trip-chicago-2016 Liabilities:US:Chase:Slate -21.64 USD Expenses:Food:Restaurant 21.64 USD 2016-11-19 * "Mercadito" "" #trip-chicago-2016 Liabilities:US:Chase:Slate -42.08 USD Expenses:Food:Restaurant 42.08 USD 2016-11-19 * "25 Degrees Burger Bar" "" #trip-chicago-2016 Liabilities:US:Chase:Slate -22.55 USD Expenses:Food:Restaurant 22.55 USD 2016-11-20 * "Mercadito" "" #trip-chicago-2016 Liabilities:US:Chase:Slate -39.85 USD Expenses:Food:Restaurant 39.85 USD 2016-11-20 * "Eataly Chicago" "" #trip-chicago-2016 Liabilities:US:Chase:Slate -36.22 USD Expenses:Food:Restaurant 36.22 USD 2016-11-21 * "Argo Tea" "" #trip-chicago-2016 Liabilities:US:Chase:Slate -5.73 USD Expenses:Food:Coffee 5.73 USD 2016-11-22 * "Another Sports Pub" "" #trip-chicago-2016 Liabilities:US:Chase:Slate -11.74 USD Expenses:Food:Alcohol 11.74 USD 2016-11-22 * "Argo Tea" "" #trip-chicago-2016 Liabilities:US:Chase:Slate -6.91 USD Expenses:Food:Coffee 6.91 USD 2016-11-23 * "25 Degrees Burger Bar" "" #trip-chicago-2016 Liabilities:US:Chase:Slate -23.44 USD Expenses:Food:Restaurant 23.44 USD 2016-11-24 balance Liabilities:US:Chase:Slate -1133.19 USD 2016-11-24 * "Eataly Chicago" "" #trip-chicago-2016 Liabilities:US:Chase:Slate -44.41 USD Expenses:Food:Restaurant 44.41 USD 2016-11-25 * "Mercadito" "" #trip-chicago-2016 Liabilities:US:Chase:Slate -38.25 USD Expenses:Food:Restaurant 38.25 USD 2016-11-26 * "Star of Siam" "" #trip-chicago-2016 Liabilities:US:Chase:Slate -28.12 USD Expenses:Food:Restaurant 28.12 USD 2016-11-26 * "Another Sports Pub" "" #trip-chicago-2016 Liabilities:US:Chase:Slate -8.77 USD Expenses:Food:Alcohol 8.77 USD 2016-11-27 * "25 Degrees Burger Bar" "" #trip-chicago-2016 Liabilities:US:Chase:Slate -25.71 USD Expenses:Food:Restaurant 25.71 USD 2016-11-27 * "Eataly Chicago" "" #trip-chicago-2016 Liabilities:US:Chase:Slate -40.06 USD Expenses:Food:Restaurant 40.06 USD 2016-11-27 * "Another Sports Pub" "" #trip-chicago-2016 Liabilities:US:Chase:Slate -8.18 USD Expenses:Food:Alcohol 8.18 USD 2016-11-28 * "Mercadito" "" #trip-chicago-2016 Liabilities:US:Chase:Slate -39.89 USD Expenses:Food:Restaurant 39.89 USD 2016-11-29 * "China Garden" "Eating out with Bill" Liabilities:US:Chase:Slate -53.71 USD Expenses:Food:Restaurant 53.71 USD 2016-11-29 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -107.91 USD Expenses:Food:Groceries 107.91 USD 2016-11-29 * "Consume vacation days" Assets:US:Hoogle:Vacation -104 VACHR Expenses:Vacation 104 VACHR 2016-11-29 event "location" "New Metropolis" 2016-12-03 * "Goba Goba" "Eating out alone" Liabilities:US:Chase:Slate -36.54 USD Expenses:Food:Restaurant 36.54 USD 2016-12-05 * "Jewel of Morroco" "Eating out with Natasha" Liabilities:US:Chase:Slate -25.96 USD Expenses:Food:Restaurant 25.96 USD 2016-12-05 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -104.09 USD Expenses:Food:Groceries 104.09 USD 2016-12-09 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -19.46 USD Expenses:Food:Restaurant 19.46 USD 2016-12-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 399.93 USD Assets:US:BofA:Checking -399.93 USD 2016-12-14 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -30.90 USD Expenses:Food:Restaurant 30.90 USD 2016-12-16 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -119.27 USD Expenses:Food:Groceries 119.27 USD 2016-12-17 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -45.48 USD Expenses:Food:Restaurant 45.48 USD 2016-12-20 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2016-12-21 balance Liabilities:US:Chase:Slate -1629.97 USD 2016-12-22 * "Kin Soy" "Eating out with work buddies" Liabilities:US:Chase:Slate -34.80 USD Expenses:Food:Restaurant 34.80 USD 2016-12-27 * "Kin Soy" "Eating out with Bill" Liabilities:US:Chase:Slate -27.77 USD Expenses:Food:Restaurant 27.77 USD 2016-12-28 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -53.44 USD Expenses:Food:Groceries 53.44 USD 2016-12-31 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -22.90 USD Expenses:Food:Restaurant 22.90 USD 2017-01-04 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -39.14 USD Expenses:Food:Restaurant 39.14 USD 2017-01-07 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -39.95 USD Expenses:Food:Restaurant 39.95 USD 2017-01-11 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -83.39 USD Expenses:Food:Groceries 83.39 USD 2017-01-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 586.14 USD Assets:US:BofA:Checking -586.14 USD 2017-01-12 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -24.80 USD Expenses:Food:Restaurant 24.80 USD 2017-01-14 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -25.58 USD Expenses:Food:Restaurant 25.58 USD 2017-01-15 balance Liabilities:US:Chase:Slate -1395.60 USD 2017-01-16 * "Uncle Boons" "Eating out with Bill" Liabilities:US:Chase:Slate -23.00 USD Expenses:Food:Restaurant 23.00 USD 2017-01-17 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2017-01-20 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -41.27 USD Expenses:Food:Restaurant 41.27 USD 2017-01-25 * "Uncle Boons" "Eating out with work buddies" Liabilities:US:Chase:Slate -21.34 USD Expenses:Food:Restaurant 21.34 USD 2017-01-26 * "China Garden" "Eating out with Bill" Liabilities:US:Chase:Slate -20.28 USD Expenses:Food:Restaurant 20.28 USD 2017-01-27 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -98.19 USD Expenses:Food:Groceries 98.19 USD 2017-01-29 * "China Garden" "Eating out with work buddies" Liabilities:US:Chase:Slate -20.88 USD Expenses:Food:Restaurant 20.88 USD 2017-02-01 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -46.67 USD Expenses:Food:Restaurant 46.67 USD 2017-02-02 * "Rose Flower" "Eating out with work buddies" Liabilities:US:Chase:Slate -75.11 USD Expenses:Food:Restaurant 75.11 USD 2017-02-07 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -30.94 USD Expenses:Food:Restaurant 30.94 USD 2017-02-10 balance Liabilities:US:Chase:Slate -1893.28 USD 2017-02-11 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -49.98 USD Expenses:Food:Restaurant 49.98 USD 2017-02-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 598.04 USD Assets:US:BofA:Checking -598.04 USD 2017-02-12 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -97.27 USD Expenses:Food:Groceries 97.27 USD 2017-02-13 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2017-02-16 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -30.31 USD Expenses:Food:Restaurant 30.31 USD 2017-02-18 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -44.29 USD Expenses:Food:Restaurant 44.29 USD 2017-02-19 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -94.71 USD Expenses:Food:Groceries 94.71 USD 2017-02-21 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -32.25 USD Expenses:Food:Restaurant 32.25 USD 2017-02-24 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -38.48 USD Expenses:Food:Restaurant 38.48 USD 2017-02-28 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -29.44 USD Expenses:Food:Restaurant 29.44 USD 2017-03-02 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -84.20 USD Expenses:Food:Groceries 84.20 USD 2017-03-04 balance Liabilities:US:Chase:Slate -1916.17 USD 2017-03-05 * "Jewel of Morroco" "Eating out with Natasha" Liabilities:US:Chase:Slate -33.74 USD Expenses:Food:Restaurant 33.74 USD 2017-03-10 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -26.72 USD Expenses:Food:Restaurant 26.72 USD 2017-03-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 631.41 USD Assets:US:BofA:Checking -631.41 USD 2017-03-13 * "Chichipotle" "Eating out with Natasha" Liabilities:US:Chase:Slate -16.23 USD Expenses:Food:Restaurant 16.23 USD 2017-03-14 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2017-03-15 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -33.88 USD Expenses:Food:Restaurant 33.88 USD 2017-03-17 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -47.56 USD Expenses:Food:Restaurant 47.56 USD 2017-03-17 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -69.48 USD Expenses:Food:Groceries 69.48 USD 2017-03-18 * "Cafe Modagor" "Eating out with Bill" Liabilities:US:Chase:Slate -38.49 USD Expenses:Food:Restaurant 38.49 USD 2017-03-22 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -18.56 USD Expenses:Food:Restaurant 18.56 USD 2017-03-24 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -21.04 USD Expenses:Food:Restaurant 21.04 USD 2017-03-27 * "Goba Goba" "Eating out " Liabilities:US:Chase:Slate -17.33 USD Expenses:Food:Restaurant 17.33 USD 2017-03-28 * "Kin Soy" "Eating out after work" Liabilities:US:Chase:Slate -29.61 USD Expenses:Food:Restaurant 29.61 USD 2017-03-30 balance Liabilities:US:Chase:Slate -1757.40 USD 2017-03-30 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -49.73 USD Expenses:Food:Restaurant 49.73 USD 2017-04-01 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -90.73 USD Expenses:Food:Groceries 90.73 USD 2017-04-03 * "China Garden" "Eating out with Bill" Liabilities:US:Chase:Slate -44.17 USD Expenses:Food:Restaurant 44.17 USD 2017-04-07 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -21.67 USD Expenses:Food:Restaurant 21.67 USD 2017-04-08 * "Cafe Modagor" "Eating out with Bill" Liabilities:US:Chase:Slate -37.15 USD Expenses:Food:Restaurant 37.15 USD 2017-04-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 775.63 USD Assets:US:BofA:Checking -775.63 USD 2017-04-11 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2017-04-12 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -20.51 USD Expenses:Food:Restaurant 20.51 USD 2017-04-15 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -15.40 USD Expenses:Food:Restaurant 15.40 USD 2017-04-16 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -24.63 USD Expenses:Food:Restaurant 24.63 USD 2017-04-19 balance Liabilities:US:Chase:Slate -1405.76 USD 2017-04-19 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -90.67 USD Expenses:Food:Groceries 90.67 USD 2017-04-20 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -21.78 USD Expenses:Food:Restaurant 21.78 USD 2017-04-22 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -15.90 USD Expenses:Food:Restaurant 15.90 USD 2017-04-23 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -22.11 USD Expenses:Food:Restaurant 22.11 USD 2017-04-25 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -24.54 USD Expenses:Food:Restaurant 24.54 USD 2017-04-27 * "Kin Soy" "Eating out with work buddies" Liabilities:US:Chase:Slate -27.92 USD Expenses:Food:Restaurant 27.92 USD 2017-04-30 * "Cafe Modagor" "Eating out with Bill" Liabilities:US:Chase:Slate -27.80 USD Expenses:Food:Restaurant 27.80 USD 2017-05-03 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -38.02 USD Expenses:Food:Restaurant 38.02 USD 2017-05-05 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -73.44 USD Expenses:Food:Groceries 73.44 USD 2017-05-06 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -23.30 USD Expenses:Food:Restaurant 23.30 USD 2017-05-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 497.48 USD Assets:US:BofA:Checking -497.48 USD 2017-05-11 * "Goba Goba" "Eating out " Liabilities:US:Chase:Slate -71.46 USD Expenses:Food:Restaurant 71.46 USD 2017-05-12 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2017-05-16 * "Jewel of Morroco" "Eating out with Natasha" Liabilities:US:Chase:Slate -44.12 USD Expenses:Food:Restaurant 44.12 USD 2017-05-16 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -88.39 USD Expenses:Food:Groceries 88.39 USD 2017-05-18 event "location" "Boston" 2017-05-19 balance Liabilities:US:Chase:Slate -1597.73 USD 2017-05-20 * "Franklin Cafe" "" #trip-boston-2017 Liabilities:US:Chase:Slate -33.76 USD Expenses:Food:Restaurant 33.76 USD 2017-05-21 * "Franklin Cafe" "" #trip-boston-2017 Liabilities:US:Chase:Slate -31.70 USD Expenses:Food:Restaurant 31.70 USD 2017-05-22 * "Legal Seafood" "" #trip-boston-2017 Liabilities:US:Chase:Slate -32.23 USD Expenses:Food:Restaurant 32.23 USD 2017-05-24 * "Franklin Cafe" "" #trip-boston-2017 Liabilities:US:Chase:Slate -24.59 USD Expenses:Food:Restaurant 24.59 USD 2017-05-25 * "Giacomo's Restaurant" "" #trip-boston-2017 Liabilities:US:Chase:Slate -38.36 USD Expenses:Food:Restaurant 38.36 USD 2017-05-25 * "Franklin Cafe" "" #trip-boston-2017 Liabilities:US:Chase:Slate -24.67 USD Expenses:Food:Restaurant 24.67 USD 2017-05-25 * "Starbucks" "" #trip-boston-2017 Liabilities:US:Chase:Slate -6.14 USD Expenses:Food:Coffee 6.14 USD 2017-05-26 * "Giacomo's Restaurant" "" #trip-boston-2017 Liabilities:US:Chase:Slate -45.67 USD Expenses:Food:Restaurant 45.67 USD 2017-05-28 * "Legal Seafood" "" #trip-boston-2017 Liabilities:US:Chase:Slate -34.51 USD Expenses:Food:Restaurant 34.51 USD 2017-05-29 * "Giacomo's Restaurant" "" #trip-boston-2017 Liabilities:US:Chase:Slate -40.18 USD Expenses:Food:Restaurant 40.18 USD 2017-05-29 * "Legal Seafood" "" #trip-boston-2017 Liabilities:US:Chase:Slate -35.09 USD Expenses:Food:Restaurant 35.09 USD 2017-05-29 * "Franklin Cafe" "" #trip-boston-2017 Liabilities:US:Chase:Slate -32.92 USD Expenses:Food:Restaurant 32.92 USD 2017-05-30 * "Franklin Cafe" "" #trip-boston-2017 Liabilities:US:Chase:Slate -27.07 USD Expenses:Food:Restaurant 27.07 USD 2017-05-31 * "Giacomo's Restaurant" "" #trip-boston-2017 Liabilities:US:Chase:Slate -43.16 USD Expenses:Food:Restaurant 43.16 USD 2017-05-31 * "Starbucks" "" #trip-boston-2017 Liabilities:US:Chase:Slate -6.16 USD Expenses:Food:Coffee 6.16 USD 2017-06-01 * "Starbucks" "" #trip-boston-2017 Liabilities:US:Chase:Slate -5.54 USD Expenses:Food:Coffee 5.54 USD 2017-06-02 * "Giacomo's Restaurant" "" #trip-boston-2017 Liabilities:US:Chase:Slate -36.43 USD Expenses:Food:Restaurant 36.43 USD 2017-06-03 * "Legal Seafood" "" #trip-boston-2017 Liabilities:US:Chase:Slate -33.82 USD Expenses:Food:Restaurant 33.82 USD 2017-06-03 * "Starbucks" "" #trip-boston-2017 Liabilities:US:Chase:Slate -7.25 USD Expenses:Food:Coffee 7.25 USD 2017-06-03 * "Consume vacation days" Assets:US:Hoogle:Vacation -128 VACHR Expenses:Vacation 128 VACHR 2017-06-03 event "location" "New Metropolis" 2017-06-04 * "Uncle Boons" "Eating out " Liabilities:US:Chase:Slate -32.97 USD Expenses:Food:Restaurant 32.97 USD 2017-06-05 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -32.80 USD Expenses:Food:Restaurant 32.80 USD 2017-06-09 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -47.98 USD Expenses:Food:Restaurant 47.98 USD 2017-06-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 392.77 USD Assets:US:BofA:Checking -392.77 USD 2017-06-12 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -26.51 USD Expenses:Food:Restaurant 26.51 USD 2017-06-14 balance Liabilities:US:Chase:Slate -1884.47 USD 2017-06-14 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2017-06-15 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -120.27 USD Expenses:Food:Groceries 120.27 USD 2017-06-17 * "Jewel of Morroco" "Eating out with Julie" Liabilities:US:Chase:Slate -26.54 USD Expenses:Food:Restaurant 26.54 USD 2017-06-21 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -38.91 USD Expenses:Food:Restaurant 38.91 USD 2017-06-26 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -54.55 USD Expenses:Food:Restaurant 54.55 USD 2017-06-30 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -43.05 USD Expenses:Food:Restaurant 43.05 USD 2017-07-01 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -123.77 USD Expenses:Food:Groceries 123.77 USD 2017-07-03 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -41.35 USD Expenses:Food:Restaurant 41.35 USD 2017-07-08 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -52.48 USD Expenses:Food:Restaurant 52.48 USD 2017-07-09 balance Liabilities:US:Chase:Slate -2505.39 USD 2017-07-09 * "Jewel of Morroco" "Eating out with work buddies" Liabilities:US:Chase:Slate -37.80 USD Expenses:Food:Restaurant 37.80 USD 2017-07-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 687.03 USD Assets:US:BofA:Checking -687.03 USD 2017-07-12 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -28.31 USD Expenses:Food:Restaurant 28.31 USD 2017-07-14 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -13.54 USD Expenses:Food:Restaurant 13.54 USD 2017-07-14 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -96.11 USD Expenses:Food:Groceries 96.11 USD 2017-07-17 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2017-07-19 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -18.54 USD Expenses:Food:Restaurant 18.54 USD 2017-07-24 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -39.01 USD Expenses:Food:Restaurant 39.01 USD 2017-07-25 * "China Garden" "Eating out with Julie" Liabilities:US:Chase:Slate -53.38 USD Expenses:Food:Restaurant 53.38 USD 2017-07-29 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -20.83 USD Expenses:Food:Restaurant 20.83 USD 2017-07-30 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -70.59 USD Expenses:Food:Groceries 70.59 USD 2017-07-31 balance Liabilities:US:Chase:Slate -2316.47 USD 2017-08-02 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -32.13 USD Expenses:Food:Restaurant 32.13 USD 2017-08-06 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -41.43 USD Expenses:Food:Restaurant 41.43 USD 2017-08-10 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -37.95 USD Expenses:Food:Restaurant 37.95 USD 2017-08-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 543.51 USD Assets:US:BofA:Checking -543.51 USD 2017-08-11 * "Chichipotle" "Eating out with work buddies" Liabilities:US:Chase:Slate -40.04 USD Expenses:Food:Restaurant 40.04 USD 2017-08-13 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -28.34 USD Expenses:Food:Restaurant 28.34 USD 2017-08-16 * "Jewel of Morroco" "Eating out with work buddies" Liabilities:US:Chase:Slate -87.42 USD Expenses:Food:Restaurant 87.42 USD 2017-08-17 * "Uncle Boons" "Eating out with Julie" Liabilities:US:Chase:Slate -32.63 USD Expenses:Food:Restaurant 32.63 USD 2017-08-18 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2017-08-19 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -73.62 USD Expenses:Food:Groceries 73.62 USD 2017-08-20 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -20.96 USD Expenses:Food:Restaurant 20.96 USD 2017-08-23 * "Uncle Boons" "Eating out with Julie" Liabilities:US:Chase:Slate -18.98 USD Expenses:Food:Restaurant 18.98 USD 2017-08-26 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -72.77 USD Expenses:Food:Restaurant 72.77 USD 2017-08-28 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -58.39 USD Expenses:Food:Restaurant 58.39 USD 2017-08-29 balance Liabilities:US:Chase:Slate -2437.62 USD 2017-08-30 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -73.96 USD Expenses:Food:Groceries 73.96 USD 2017-09-01 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -37.51 USD Expenses:Food:Restaurant 37.51 USD 2017-09-04 * "Uncle Boons" "Eating out with Bill" Liabilities:US:Chase:Slate -34.18 USD Expenses:Food:Restaurant 34.18 USD 2017-09-06 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -86.31 USD Expenses:Food:Groceries 86.31 USD 2017-09-08 * "Cafe Modagor" "Eating out with Julie" Liabilities:US:Chase:Slate -33.71 USD Expenses:Food:Restaurant 33.71 USD * Taxable Investments 2015-01-01 open Assets:US:ETrade:Cash USD 2015-01-01 open Assets:US:ETrade:ITOT ITOT 2015-01-01 open Assets:US:ETrade:VEA VEA 2015-01-01 open Assets:US:ETrade:VHT VHT 2015-01-01 open Assets:US:ETrade:GLD GLD 2015-01-01 open Income:US:ETrade:Gains USD 2015-01-01 open Income:US:ETrade:Dividends USD 2015-09-15 * "Buy shares of VEA" Assets:US:ETrade:Cash -1072.39 USD Assets:US:ETrade:VEA 8 VEA {132.93 USD, 2015-09-15} Expenses:Financial:Commissions 8.95 USD 2015-09-15 * "Buy shares of ITOT" Assets:US:ETrade:Cash -1068.43 USD Assets:US:ETrade:ITOT 18 ITOT {58.86 USD, 2015-09-15} Expenses:Financial:Commissions 8.95 USD 2015-09-15 * "Buy shares of VHT" Assets:US:ETrade:Cash -1034.14 USD Assets:US:ETrade:VHT 9 VHT {113.91 USD, 2015-09-15} Expenses:Financial:Commissions 8.95 USD 2015-09-15 * "Buy shares of GLD" Assets:US:ETrade:Cash -994.25 USD Assets:US:ETrade:GLD 5 GLD {197.06 USD, 2015-09-15} Expenses:Financial:Commissions 8.95 USD 2015-09-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 16.53 USD Income:US:ETrade:Dividends -16.53 USD 2015-11-13 * "Buy shares of VHT" Assets:US:ETrade:Cash -2028.73 USD Assets:US:ETrade:VHT 18 VHT {112.21 USD, 2015-11-13} Expenses:Financial:Commissions 8.95 USD 2015-11-13 * "Buy shares of GLD" Assets:US:ETrade:Cash -2098.95 USD Assets:US:ETrade:GLD 10 GLD {209.00 USD, 2015-11-13} Expenses:Financial:Commissions 8.95 USD 2015-11-18 * "Sell shares of GLD" Assets:US:ETrade:GLD -5 GLD {197.06 USD, 2015-09-15} @ 209.00 USD Assets:US:ETrade:Cash 1036.05 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -59.70 USD 2015-12-17 * "Dividends on portfolio" Assets:US:ETrade:Cash 32.97 USD Income:US:ETrade:Dividends -32.97 USD 2015-12-30 * "Buy shares of VEA" Assets:US:ETrade:Cash -1104.63 USD Assets:US:ETrade:VEA 8 VEA {136.96 USD, 2015-12-30} Expenses:Financial:Commissions 8.95 USD 2016-03-17 * "Dividends on portfolio" Assets:US:ETrade:Cash 37.36 USD Income:US:ETrade:Dividends -37.36 USD 2016-04-29 * "Sell shares of VHT" Assets:US:ETrade:VHT -18 VHT {112.21 USD, 2015-11-13} @ 118.03 USD Assets:US:ETrade:Cash 2115.59 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -104.76 USD 2016-05-05 * "Buy shares of VEA" Assets:US:ETrade:Cash -412.84 USD Assets:US:ETrade:VEA 3 VEA {134.63 USD, 2016-05-05} Expenses:Financial:Commissions 8.95 USD 2016-05-05 * "Buy shares of ITOT" Assets:US:ETrade:Cash -506.55 USD Assets:US:ETrade:ITOT 8 ITOT {62.20 USD, 2016-05-05} Expenses:Financial:Commissions 8.95 USD 2016-05-05 * "Buy shares of GLD" Assets:US:ETrade:Cash -420.51 USD Assets:US:ETrade:GLD 2 GLD {205.78 USD, 2016-05-05} Expenses:Financial:Commissions 8.95 USD 2016-05-05 * "Buy shares of VHT" Assets:US:ETrade:Cash -481.07 USD Assets:US:ETrade:VHT 4 VHT {118.03 USD, 2016-05-05} Expenses:Financial:Commissions 8.95 USD 2016-06-17 * "Dividends on portfolio" Assets:US:ETrade:Cash 44.50 USD Income:US:ETrade:Dividends -44.50 USD 2016-07-25 * "Sell shares of VHT" Assets:US:ETrade:VHT -4 VHT {118.03 USD, 2016-05-05} @ 118.19 USD Assets:US:ETrade:Cash 463.81 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -0.64 USD 2016-08-13 * "Buy shares of VHT" Assets:US:ETrade:Cash -2405.95 USD Assets:US:ETrade:VHT 20 VHT {119.85 USD, 2016-08-13} Expenses:Financial:Commissions 8.95 USD 2016-08-13 * "Buy shares of VEA" Assets:US:ETrade:Cash -2405.44 USD Assets:US:ETrade:VEA 17 VEA {140.97 USD, 2016-08-13} Expenses:Financial:Commissions 8.95 USD 2016-09-17 * "Dividends on portfolio" Assets:US:ETrade:Cash 63.67 USD Income:US:ETrade:Dividends -63.67 USD 2016-09-23 * "Buy shares of ITOT" Assets:US:ETrade:Cash -1252.88 USD Assets:US:ETrade:ITOT 19 ITOT {65.47 USD, 2016-09-23} Expenses:Financial:Commissions 8.95 USD 2016-09-23 * "Buy shares of GLD" Assets:US:ETrade:Cash -1165.85 USD Assets:US:ETrade:GLD 5 GLD {231.38 USD, 2016-09-23} Expenses:Financial:Commissions 8.95 USD 2016-09-23 * "Buy shares of VHT" Assets:US:ETrade:Cash -1200.65 USD Assets:US:ETrade:VHT 10 VHT {119.17 USD, 2016-09-23} Expenses:Financial:Commissions 8.95 USD 2016-11-23 * "Buy shares of ITOT" Assets:US:ETrade:Cash -3362.45 USD Assets:US:ETrade:ITOT 50 ITOT {67.07 USD, 2016-11-23} Expenses:Financial:Commissions 8.95 USD 2016-12-17 * "Dividends on portfolio" Assets:US:ETrade:Cash 91.45 USD Income:US:ETrade:Dividends -91.45 USD 2017-02-15 * "Sell shares of GLD" Assets:US:ETrade:GLD -10 GLD {209.00 USD, 2015-11-13} @ 273.28 USD Assets:US:ETrade:Cash 2723.85 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -642.80 USD 2017-03-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 91.45 USD Income:US:ETrade:Dividends -91.45 USD 2017-03-19 * "Buy shares of VEA" Assets:US:ETrade:Cash -603.31 USD Assets:US:ETrade:VEA 4 VEA {148.59 USD, 2017-03-19} Expenses:Financial:Commissions 8.95 USD 2017-03-19 * "Buy shares of ITOT" Assets:US:ETrade:Cash -683.32 USD Assets:US:ETrade:ITOT 9 ITOT {74.93 USD, 2017-03-19} Expenses:Financial:Commissions 8.95 USD 2017-03-19 * "Buy shares of VHT" Assets:US:ETrade:Cash -702.25 USD Assets:US:ETrade:VHT 6 VHT {115.55 USD, 2017-03-19} Expenses:Financial:Commissions 8.95 USD 2017-03-19 * "Buy shares of GLD" Assets:US:ETrade:Cash -566.93 USD Assets:US:ETrade:GLD 2 GLD {278.99 USD, 2017-03-19} Expenses:Financial:Commissions 8.95 USD 2017-06-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 101.53 USD Income:US:ETrade:Dividends -101.53 USD 2017-08-22 * "Sell shares of VEA" Assets:US:ETrade:VEA -4 VEA {148.59 USD, 2017-03-19} @ 145.72 USD Assets:US:ETrade:Cash 573.93 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains 11.48 USD 2017-08-27 * "Buy shares of GLD" Assets:US:ETrade:Cash -2442.31 USD Assets:US:ETrade:GLD 8 GLD {304.17 USD, 2017-08-27} Expenses:Financial:Commissions 8.95 USD 2017-08-27 * "Buy shares of VHT" Assets:US:ETrade:Cash -2523.03 USD Assets:US:ETrade:VHT 19 VHT {132.32 USD, 2017-08-27} Expenses:Financial:Commissions 8.95 USD * Vanguard Investments 2015-01-01 open Assets:US:Vanguard:VBMPX VBMPX number: "882882" 2015-01-01 open Assets:US:Vanguard:RGAGX RGAGX number: "882882" 2015-01-01 open Assets:US:Vanguard USD address: "P.O. Box 1110, Valley Forge, PA 19482-1110" institution: "Vanguard Group" phone: "+1.800.523.1188" 2015-01-01 open Income:US:Hoogle:Match401k USD 2015-01-01 open Assets:US:Vanguard:Cash USD number: "882882" 2015-01-02 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2015-01-05 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.513 VBMPX {136.65 USD, 2015-01-05} Assets:US:Vanguard:Cash -480.05 USD 2015-01-05 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.175 RGAGX {88.07 USD, 2015-01-05} Assets:US:Vanguard:Cash -719.97 USD 2015-01-05 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.756 VBMPX {136.65 USD, 2015-01-05} Assets:US:Vanguard:Cash -239.96 USD 2015-01-05 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.088 RGAGX {88.07 USD, 2015-01-05} Assets:US:Vanguard:Cash -360.03 USD 2015-01-16 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2015-01-19 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.469 VBMPX {138.35 USD, 2015-01-19} Assets:US:Vanguard:Cash -479.94 USD 2015-01-19 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.102 RGAGX {88.87 USD, 2015-01-19} Assets:US:Vanguard:Cash -720.02 USD 2015-01-19 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.735 VBMPX {138.35 USD, 2015-01-19} Assets:US:Vanguard:Cash -240.04 USD 2015-01-19 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.051 RGAGX {88.87 USD, 2015-01-19} Assets:US:Vanguard:Cash -360.01 USD 2015-01-30 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2015-02-02 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.430 VBMPX {139.94 USD, 2015-02-02} Assets:US:Vanguard:Cash -479.99 USD 2015-02-02 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.196 RGAGX {87.85 USD, 2015-02-02} Assets:US:Vanguard:Cash -720.02 USD 2015-02-02 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.715 VBMPX {139.94 USD, 2015-02-02} Assets:US:Vanguard:Cash -240.00 USD 2015-02-02 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.098 RGAGX {87.85 USD, 2015-02-02} Assets:US:Vanguard:Cash -360.01 USD 2015-02-13 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2015-02-16 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.420 VBMPX {140.34 USD, 2015-02-16} Assets:US:Vanguard:Cash -479.96 USD 2015-02-16 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.161 RGAGX {88.22 USD, 2015-02-16} Assets:US:Vanguard:Cash -719.96 USD 2015-02-16 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.710 VBMPX {140.34 USD, 2015-02-16} Assets:US:Vanguard:Cash -239.98 USD 2015-02-16 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.081 RGAGX {88.22 USD, 2015-02-16} Assets:US:Vanguard:Cash -360.03 USD 2015-02-27 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2015-03-02 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.411 VBMPX {140.74 USD, 2015-03-02} Assets:US:Vanguard:Cash -480.06 USD 2015-03-02 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.312 RGAGX {86.62 USD, 2015-03-02} Assets:US:Vanguard:Cash -719.99 USD 2015-03-02 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.705 VBMPX {140.74 USD, 2015-03-02} Assets:US:Vanguard:Cash -239.96 USD 2015-03-02 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.156 RGAGX {86.62 USD, 2015-03-02} Assets:US:Vanguard:Cash -359.99 USD 2015-03-13 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2015-03-16 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.376 VBMPX {142.20 USD, 2015-03-16} Assets:US:Vanguard:Cash -480.07 USD 2015-03-16 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.358 RGAGX {86.15 USD, 2015-03-16} Assets:US:Vanguard:Cash -720.04 USD 2015-03-16 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.688 VBMPX {142.20 USD, 2015-03-16} Assets:US:Vanguard:Cash -240.03 USD 2015-03-16 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.178 RGAGX {86.15 USD, 2015-03-16} Assets:US:Vanguard:Cash -359.93 USD 2015-03-27 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2015-03-30 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.393 VBMPX {141.48 USD, 2015-03-30} Assets:US:Vanguard:Cash -480.04 USD 2015-03-30 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.507 RGAGX {84.63 USD, 2015-03-30} Assets:US:Vanguard:Cash -719.95 USD 2015-03-30 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.696 VBMPX {141.48 USD, 2015-03-30} Assets:US:Vanguard:Cash -239.95 USD 2015-03-30 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.254 RGAGX {84.63 USD, 2015-03-30} Assets:US:Vanguard:Cash -360.02 USD 2015-04-10 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2015-04-13 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.343 VBMPX {143.57 USD, 2015-04-13} Assets:US:Vanguard:Cash -479.95 USD 2015-04-13 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.605 RGAGX {83.67 USD, 2015-04-13} Assets:US:Vanguard:Cash -719.98 USD 2015-04-13 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.672 VBMPX {143.57 USD, 2015-04-13} Assets:US:Vanguard:Cash -240.05 USD 2015-04-13 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.303 RGAGX {83.67 USD, 2015-04-13} Assets:US:Vanguard:Cash -360.03 USD 2015-04-24 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2015-04-27 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.310 VBMPX {145.00 USD, 2015-04-27} Assets:US:Vanguard:Cash -479.95 USD 2015-04-27 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.753 RGAGX {82.26 USD, 2015-04-27} Assets:US:Vanguard:Cash -720.02 USD 2015-04-27 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.655 VBMPX {145.00 USD, 2015-04-27} Assets:US:Vanguard:Cash -239.98 USD 2015-04-27 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.377 RGAGX {82.26 USD, 2015-04-27} Assets:US:Vanguard:Cash -360.05 USD 2015-05-08 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2015-05-11 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.251 VBMPX {147.63 USD, 2015-05-11} Assets:US:Vanguard:Cash -479.95 USD 2015-05-11 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 9.095 RGAGX {79.16 USD, 2015-05-11} Assets:US:Vanguard:Cash -719.96 USD 2015-05-11 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.626 VBMPX {147.63 USD, 2015-05-11} Assets:US:Vanguard:Cash -240.05 USD 2015-05-11 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.548 RGAGX {79.16 USD, 2015-05-11} Assets:US:Vanguard:Cash -360.02 USD 2015-05-22 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2015-05-25 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.231 VBMPX {148.55 USD, 2015-05-25} Assets:US:Vanguard:Cash -479.97 USD 2015-05-25 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 9.341 RGAGX {77.08 USD, 2015-05-25} Assets:US:Vanguard:Cash -720.00 USD 2015-05-25 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.616 VBMPX {148.55 USD, 2015-05-25} Assets:US:Vanguard:Cash -240.06 USD 2015-05-25 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.671 RGAGX {77.08 USD, 2015-05-25} Assets:US:Vanguard:Cash -360.04 USD 2015-06-05 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2015-06-08 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.223 VBMPX {148.91 USD, 2015-06-08} Assets:US:Vanguard:Cash -479.94 USD 2015-06-08 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 9.422 RGAGX {76.41 USD, 2015-06-08} Assets:US:Vanguard:Cash -719.94 USD 2015-06-08 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.612 VBMPX {148.91 USD, 2015-06-08} Assets:US:Vanguard:Cash -240.04 USD 2015-06-08 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.712 RGAGX {76.41 USD, 2015-06-08} Assets:US:Vanguard:Cash -360.04 USD 2015-06-19 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2015-06-22 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.200 VBMPX {150.01 USD, 2015-06-22} Assets:US:Vanguard:Cash -480.03 USD 2015-06-22 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 9.442 RGAGX {76.25 USD, 2015-06-22} Assets:US:Vanguard:Cash -719.95 USD 2015-06-22 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.600 VBMPX {150.01 USD, 2015-06-22} Assets:US:Vanguard:Cash -240.02 USD 2015-06-22 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.721 RGAGX {76.25 USD, 2015-06-22} Assets:US:Vanguard:Cash -359.98 USD 2015-07-03 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2015-07-06 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.226 VBMPX {148.80 USD, 2015-07-06} Assets:US:Vanguard:Cash -480.03 USD 2015-07-06 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 9.384 RGAGX {76.73 USD, 2015-07-06} Assets:US:Vanguard:Cash -720.03 USD 2015-07-06 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.613 VBMPX {148.80 USD, 2015-07-06} Assets:US:Vanguard:Cash -240.01 USD 2015-07-06 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.691 RGAGX {76.73 USD, 2015-07-06} Assets:US:Vanguard:Cash -359.94 USD 2015-07-17 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2015-07-20 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.174 VBMPX {151.23 USD, 2015-07-20} Assets:US:Vanguard:Cash -480.00 USD 2015-07-20 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 9.359 RGAGX {76.93 USD, 2015-07-20} Assets:US:Vanguard:Cash -719.99 USD 2015-07-20 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.587 VBMPX {151.23 USD, 2015-07-20} Assets:US:Vanguard:Cash -240.00 USD 2015-07-20 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.680 RGAGX {76.93 USD, 2015-07-20} Assets:US:Vanguard:Cash -360.03 USD 2016-01-15 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2016-01-18 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.886 VBMPX {166.33 USD, 2016-01-18} Assets:US:Vanguard:Cash -480.03 USD 2016-01-18 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 9.056 RGAGX {79.50 USD, 2016-01-18} Assets:US:Vanguard:Cash -719.95 USD 2016-01-18 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.443 VBMPX {166.33 USD, 2016-01-18} Assets:US:Vanguard:Cash -240.01 USD 2016-01-18 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.528 RGAGX {79.50 USD, 2016-01-18} Assets:US:Vanguard:Cash -359.98 USD 2016-01-29 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2016-02-01 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.842 VBMPX {168.90 USD, 2016-02-01} Assets:US:Vanguard:Cash -480.01 USD 2016-02-01 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 9.051 RGAGX {79.55 USD, 2016-02-01} Assets:US:Vanguard:Cash -720.01 USD 2016-02-01 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.421 VBMPX {168.90 USD, 2016-02-01} Assets:US:Vanguard:Cash -240.01 USD 2016-02-01 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.525 RGAGX {79.55 USD, 2016-02-01} Assets:US:Vanguard:Cash -359.96 USD 2016-02-12 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2016-02-15 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.841 VBMPX {168.94 USD, 2016-02-15} Assets:US:Vanguard:Cash -479.96 USD 2016-02-15 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 9.109 RGAGX {79.04 USD, 2016-02-15} Assets:US:Vanguard:Cash -719.98 USD 2016-02-15 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.421 VBMPX {168.94 USD, 2016-02-15} Assets:US:Vanguard:Cash -240.06 USD 2016-02-15 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.555 RGAGX {79.04 USD, 2016-02-15} Assets:US:Vanguard:Cash -360.03 USD 2016-02-26 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2016-02-29 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.832 VBMPX {169.50 USD, 2016-02-29} Assets:US:Vanguard:Cash -480.02 USD 2016-02-29 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 9.179 RGAGX {78.44 USD, 2016-02-29} Assets:US:Vanguard:Cash -720.00 USD 2016-02-29 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.416 VBMPX {169.50 USD, 2016-02-29} Assets:US:Vanguard:Cash -240.01 USD 2016-02-29 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.589 RGAGX {78.44 USD, 2016-02-29} Assets:US:Vanguard:Cash -359.96 USD 2016-03-11 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2016-03-14 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.784 VBMPX {172.43 USD, 2016-03-14} Assets:US:Vanguard:Cash -480.05 USD 2016-03-14 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 9.067 RGAGX {79.41 USD, 2016-03-14} Assets:US:Vanguard:Cash -720.01 USD 2016-03-14 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.392 VBMPX {172.43 USD, 2016-03-14} Assets:US:Vanguard:Cash -240.02 USD 2016-03-14 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.533 RGAGX {79.41 USD, 2016-03-14} Assets:US:Vanguard:Cash -359.97 USD 2016-03-25 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2016-03-28 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.769 VBMPX {173.31 USD, 2016-03-28} Assets:US:Vanguard:Cash -479.90 USD 2016-03-28 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.898 RGAGX {80.91 USD, 2016-03-28} Assets:US:Vanguard:Cash -719.94 USD 2016-03-28 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.385 VBMPX {173.31 USD, 2016-03-28} Assets:US:Vanguard:Cash -240.03 USD 2016-03-28 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.450 RGAGX {80.91 USD, 2016-03-28} Assets:US:Vanguard:Cash -360.05 USD 2016-04-08 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2016-04-11 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.760 VBMPX {173.94 USD, 2016-04-11} Assets:US:Vanguard:Cash -480.07 USD 2016-04-11 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.811 RGAGX {81.72 USD, 2016-04-11} Assets:US:Vanguard:Cash -720.03 USD 2016-04-11 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.380 VBMPX {173.94 USD, 2016-04-11} Assets:US:Vanguard:Cash -240.04 USD 2016-04-11 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.405 RGAGX {81.72 USD, 2016-04-11} Assets:US:Vanguard:Cash -359.98 USD 2016-04-22 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2016-04-25 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.693 VBMPX {178.22 USD, 2016-04-25} Assets:US:Vanguard:Cash -479.95 USD 2016-04-25 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.866 RGAGX {81.20 USD, 2016-04-25} Assets:US:Vanguard:Cash -719.92 USD 2016-04-25 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.347 VBMPX {178.22 USD, 2016-04-25} Assets:US:Vanguard:Cash -240.06 USD 2016-04-25 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.434 RGAGX {81.20 USD, 2016-04-25} Assets:US:Vanguard:Cash -360.04 USD 2016-05-06 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2016-05-09 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.717 VBMPX {176.67 USD, 2016-05-09} Assets:US:Vanguard:Cash -480.01 USD 2016-05-09 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.756 RGAGX {82.22 USD, 2016-05-09} Assets:US:Vanguard:Cash -719.92 USD 2016-05-09 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.358 VBMPX {176.67 USD, 2016-05-09} Assets:US:Vanguard:Cash -239.92 USD 2016-05-09 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.378 RGAGX {82.22 USD, 2016-05-09} Assets:US:Vanguard:Cash -359.96 USD 2016-05-20 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2016-05-23 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.710 VBMPX {177.15 USD, 2016-05-23} Assets:US:Vanguard:Cash -480.08 USD 2016-05-23 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.636 RGAGX {83.38 USD, 2016-05-23} Assets:US:Vanguard:Cash -720.07 USD 2016-05-23 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.355 VBMPX {177.15 USD, 2016-05-23} Assets:US:Vanguard:Cash -240.04 USD 2016-05-23 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.317 RGAGX {83.38 USD, 2016-05-23} Assets:US:Vanguard:Cash -359.95 USD 2016-06-03 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2016-06-06 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.686 VBMPX {178.72 USD, 2016-06-06} Assets:US:Vanguard:Cash -480.04 USD 2016-06-06 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.443 RGAGX {85.28 USD, 2016-06-06} Assets:US:Vanguard:Cash -720.02 USD 2016-06-06 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.343 VBMPX {178.72 USD, 2016-06-06} Assets:US:Vanguard:Cash -240.02 USD 2016-06-06 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.221 RGAGX {85.28 USD, 2016-06-06} Assets:US:Vanguard:Cash -359.97 USD 2016-06-17 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2016-06-20 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.660 VBMPX {180.41 USD, 2016-06-20} Assets:US:Vanguard:Cash -479.89 USD 2016-06-20 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.409 RGAGX {85.62 USD, 2016-06-20} Assets:US:Vanguard:Cash -719.98 USD 2016-06-20 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.330 VBMPX {180.41 USD, 2016-06-20} Assets:US:Vanguard:Cash -239.95 USD 2016-06-20 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.205 RGAGX {85.62 USD, 2016-06-20} Assets:US:Vanguard:Cash -360.03 USD 2016-07-01 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2016-07-04 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.654 VBMPX {180.86 USD, 2016-07-04} Assets:US:Vanguard:Cash -480.00 USD 2016-07-04 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.406 RGAGX {85.66 USD, 2016-07-04} Assets:US:Vanguard:Cash -720.06 USD 2016-07-04 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.327 VBMPX {180.86 USD, 2016-07-04} Assets:US:Vanguard:Cash -240.00 USD 2016-07-04 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.203 RGAGX {85.66 USD, 2016-07-04} Assets:US:Vanguard:Cash -360.03 USD 2016-07-15 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2016-07-18 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.656 VBMPX {180.75 USD, 2016-07-18} Assets:US:Vanguard:Cash -480.07 USD 2016-07-18 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.725 RGAGX {82.52 USD, 2016-07-18} Assets:US:Vanguard:Cash -719.99 USD 2016-07-18 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.328 VBMPX {180.75 USD, 2016-07-18} Assets:US:Vanguard:Cash -240.04 USD 2016-07-18 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.362 RGAGX {82.52 USD, 2016-07-18} Assets:US:Vanguard:Cash -359.95 USD 2016-07-29 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2016-08-01 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.627 VBMPX {182.73 USD, 2016-08-01} Assets:US:Vanguard:Cash -480.03 USD 2016-08-01 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.542 RGAGX {84.29 USD, 2016-08-01} Assets:US:Vanguard:Cash -720.01 USD 2016-08-01 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.313 VBMPX {182.73 USD, 2016-08-01} Assets:US:Vanguard:Cash -239.92 USD 2016-08-01 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.270 RGAGX {84.29 USD, 2016-08-01} Assets:US:Vanguard:Cash -359.92 USD 2017-01-13 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2017-01-16 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.672 VBMPX {179.68 USD, 2017-01-16} Assets:US:Vanguard:Cash -480.10 USD 2017-01-16 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.892 RGAGX {80.98 USD, 2017-01-16} Assets:US:Vanguard:Cash -720.07 USD 2017-01-16 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.335 VBMPX {179.68 USD, 2017-01-16} Assets:US:Vanguard:Cash -239.87 USD 2017-01-16 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.445 RGAGX {80.98 USD, 2017-01-16} Assets:US:Vanguard:Cash -359.96 USD 2017-01-27 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2017-01-30 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.643 VBMPX {181.60 USD, 2017-01-30} Assets:US:Vanguard:Cash -479.97 USD 2017-01-30 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.875 RGAGX {81.13 USD, 2017-01-30} Assets:US:Vanguard:Cash -720.03 USD 2017-01-30 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.322 VBMPX {181.60 USD, 2017-01-30} Assets:US:Vanguard:Cash -240.08 USD 2017-01-30 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.438 RGAGX {81.13 USD, 2017-01-30} Assets:US:Vanguard:Cash -360.05 USD 2017-02-10 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2017-02-13 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.626 VBMPX {182.75 USD, 2017-02-13} Assets:US:Vanguard:Cash -479.90 USD 2017-02-13 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.764 RGAGX {82.15 USD, 2017-02-13} Assets:US:Vanguard:Cash -719.96 USD 2017-02-13 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.313 VBMPX {182.75 USD, 2017-02-13} Assets:US:Vanguard:Cash -239.95 USD 2017-02-13 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.383 RGAGX {82.15 USD, 2017-02-13} Assets:US:Vanguard:Cash -360.06 USD 2017-02-24 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2017-02-27 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.585 VBMPX {185.73 USD, 2017-02-27} Assets:US:Vanguard:Cash -480.11 USD 2017-02-27 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.863 RGAGX {81.24 USD, 2017-02-27} Assets:US:Vanguard:Cash -720.03 USD 2017-02-27 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.292 VBMPX {185.73 USD, 2017-02-27} Assets:US:Vanguard:Cash -239.96 USD 2017-02-27 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.431 RGAGX {81.24 USD, 2017-02-27} Assets:US:Vanguard:Cash -359.97 USD 2017-03-10 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2017-03-13 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.547 VBMPX {188.46 USD, 2017-03-13} Assets:US:Vanguard:Cash -480.01 USD 2017-03-13 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.986 RGAGX {80.12 USD, 2017-03-13} Assets:US:Vanguard:Cash -719.96 USD 2017-03-13 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.274 VBMPX {188.46 USD, 2017-03-13} Assets:US:Vanguard:Cash -240.10 USD 2017-03-13 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.493 RGAGX {80.12 USD, 2017-03-13} Assets:US:Vanguard:Cash -359.98 USD 2017-03-24 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2017-03-27 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.533 VBMPX {189.47 USD, 2017-03-27} Assets:US:Vanguard:Cash -479.93 USD 2017-03-27 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.871 RGAGX {81.16 USD, 2017-03-27} Assets:US:Vanguard:Cash -719.97 USD 2017-03-27 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.267 VBMPX {189.47 USD, 2017-03-27} Assets:US:Vanguard:Cash -240.06 USD 2017-03-27 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.436 RGAGX {81.16 USD, 2017-03-27} Assets:US:Vanguard:Cash -360.03 USD 2017-04-07 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2017-04-10 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.511 VBMPX {191.16 USD, 2017-04-10} Assets:US:Vanguard:Cash -480.00 USD 2017-04-10 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.948 RGAGX {80.46 USD, 2017-04-10} Assets:US:Vanguard:Cash -719.96 USD 2017-04-10 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.255 VBMPX {191.16 USD, 2017-04-10} Assets:US:Vanguard:Cash -239.91 USD 2017-04-10 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.474 RGAGX {80.46 USD, 2017-04-10} Assets:US:Vanguard:Cash -359.98 USD 2017-04-21 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2017-04-24 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.443 VBMPX {196.50 USD, 2017-04-24} Assets:US:Vanguard:Cash -480.05 USD 2017-04-24 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.950 RGAGX {80.45 USD, 2017-04-24} Assets:US:Vanguard:Cash -720.03 USD 2017-04-24 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.221 VBMPX {196.50 USD, 2017-04-24} Assets:US:Vanguard:Cash -239.93 USD 2017-04-24 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.475 RGAGX {80.45 USD, 2017-04-24} Assets:US:Vanguard:Cash -360.01 USD 2017-05-05 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2017-05-08 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.397 VBMPX {200.23 USD, 2017-05-08} Assets:US:Vanguard:Cash -479.95 USD 2017-05-08 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.827 RGAGX {81.57 USD, 2017-05-08} Assets:US:Vanguard:Cash -720.02 USD 2017-05-08 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.199 VBMPX {200.23 USD, 2017-05-08} Assets:US:Vanguard:Cash -240.08 USD 2017-05-08 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.414 RGAGX {81.57 USD, 2017-05-08} Assets:US:Vanguard:Cash -360.05 USD 2017-05-19 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2017-05-22 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.364 VBMPX {203.04 USD, 2017-05-22} Assets:US:Vanguard:Cash -479.99 USD 2017-05-22 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.726 RGAGX {82.51 USD, 2017-05-22} Assets:US:Vanguard:Cash -719.98 USD 2017-05-22 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.182 VBMPX {203.04 USD, 2017-05-22} Assets:US:Vanguard:Cash -239.99 USD 2017-05-22 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.363 RGAGX {82.51 USD, 2017-05-22} Assets:US:Vanguard:Cash -359.99 USD 2017-06-02 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2017-06-05 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.323 VBMPX {206.64 USD, 2017-06-05} Assets:US:Vanguard:Cash -480.02 USD 2017-06-05 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.768 RGAGX {82.12 USD, 2017-06-05} Assets:US:Vanguard:Cash -720.03 USD 2017-06-05 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.161 VBMPX {206.64 USD, 2017-06-05} Assets:US:Vanguard:Cash -239.91 USD 2017-06-05 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.384 RGAGX {82.12 USD, 2017-06-05} Assets:US:Vanguard:Cash -360.01 USD 2017-06-16 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2017-06-19 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.328 VBMPX {206.18 USD, 2017-06-19} Assets:US:Vanguard:Cash -479.99 USD 2017-06-19 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.531 RGAGX {84.40 USD, 2017-06-19} Assets:US:Vanguard:Cash -720.02 USD 2017-06-19 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.164 VBMPX {206.18 USD, 2017-06-19} Assets:US:Vanguard:Cash -239.99 USD 2017-06-19 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.266 RGAGX {84.40 USD, 2017-06-19} Assets:US:Vanguard:Cash -360.05 USD 2017-06-30 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2017-07-03 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.310 VBMPX {207.83 USD, 2017-07-03} Assets:US:Vanguard:Cash -480.09 USD 2017-07-03 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.473 RGAGX {84.98 USD, 2017-07-03} Assets:US:Vanguard:Cash -720.04 USD 2017-07-03 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.155 VBMPX {207.83 USD, 2017-07-03} Assets:US:Vanguard:Cash -240.04 USD 2017-07-03 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.235 RGAGX {84.98 USD, 2017-07-03} Assets:US:Vanguard:Cash -359.89 USD 2017-07-14 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2017-07-17 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.316 VBMPX {207.28 USD, 2017-07-17} Assets:US:Vanguard:Cash -480.06 USD 2017-07-17 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.529 RGAGX {84.41 USD, 2017-07-17} Assets:US:Vanguard:Cash -719.93 USD 2017-07-17 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.158 VBMPX {207.28 USD, 2017-07-17} Assets:US:Vanguard:Cash -240.03 USD 2017-07-17 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.265 RGAGX {84.41 USD, 2017-07-17} Assets:US:Vanguard:Cash -360.01 USD 2017-07-28 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hoogle:Match401k -600.00 USD 2017-07-31 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.318 VBMPX {207.06 USD, 2017-07-31} Assets:US:Vanguard:Cash -479.97 USD 2017-07-31 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 8.643 RGAGX {83.30 USD, 2017-07-31} Assets:US:Vanguard:Cash -719.96 USD 2017-07-31 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.159 VBMPX {207.06 USD, 2017-07-31} Assets:US:Vanguard:Cash -239.98 USD 2017-07-31 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.322 RGAGX {83.30 USD, 2017-07-31} Assets:US:Vanguard:Cash -360.02 USD 2017-08-11 * "Employer match for contribution" Assets:US:Vanguard:Cash 250.00 USD Income:US:Hoogle:Match401k -250.00 USD 2017-08-14 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.958 VBMPX {208.79 USD, 2017-08-14} Assets:US:Vanguard:Cash -200.02 USD 2017-08-14 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 3.685 RGAGX {81.40 USD, 2017-08-14} Assets:US:Vanguard:Cash -299.96 USD 2017-08-14 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.479 VBMPX {208.79 USD, 2017-08-14} Assets:US:Vanguard:Cash -100.01 USD 2017-08-14 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.843 RGAGX {81.40 USD, 2017-08-14} Assets:US:Vanguard:Cash -150.02 USD * Sources of Income 2015-01-01 open Income:US:Hoogle:Salary USD 2015-01-01 open Income:US:Hoogle:GroupTermLife USD 2015-01-01 open Income:US:Hoogle:Vacation VACHR 2015-01-01 open Assets:US:Hoogle:Vacation VACHR 2015-01-01 open Expenses:Vacation VACHR 2015-01-01 open Expenses:Health:Life:GroupTermLife 2015-01-01 open Expenses:Health:Medical:Insurance 2015-01-01 open Expenses:Health:Dental:Insurance 2015-01-01 open Expenses:Health:Vision:Insurance 2015-01-01 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-01-01 event "employer" "Hoogle, 1600 Amphibious Parkway, River View, CA" 2015-01-15 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-01-29 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-02-12 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-02-26 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-03-12 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-03-26 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-04-09 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-04-23 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-05-07 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-05-21 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-06-04 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-06-18 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-07-02 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-07-16 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-07-30 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-08-13 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-08-27 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-09-10 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-09-24 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-10-08 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-10-22 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-11-05 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-11-19 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-12-03 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2589.06 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 243.08 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-12-17 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2832.14 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 0.00 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2015-12-31 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2832.14 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 0.00 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-01-14 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-01-28 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-02-11 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-02-25 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-03-10 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-03-24 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-04-07 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-04-21 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-05-05 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-05-19 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-06-02 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-06-16 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-06-30 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-07-14 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-07-28 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2016:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-08-11 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-08-25 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-09-08 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-09-22 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-10-06 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-10-20 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-11-03 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-11-17 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-12-01 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-12-15 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2589.06 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 243.08 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2016-12-29 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2832.14 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2016:US:Medicare 106.62 USD Expenses:Taxes:Y2016:US:Federal 1062.92 USD Expenses:Taxes:Y2016:US:State 365.08 USD Expenses:Taxes:Y2016:US:CityNYC 174.92 USD Expenses:Taxes:Y2016:US:SDI 1.12 USD Expenses:Taxes:Y2016:US:SocSec 0.00 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2017-01-12 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2017-01-26 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2017-02-09 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2017-02-23 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2017-03-09 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2017-03-23 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2017-04-06 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2017-04-20 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2017-05-04 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2017-05-18 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2017-06-01 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2017-06-15 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2017-06-29 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2017-07-13 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2017-07-27 * "Hoogle" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2017-08-10 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2050.60 USD Assets:US:Vanguard:Cash 500.00 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -500.00 IRAUSD Expenses:Taxes:Y2017:US:Federal:PreTax401k 500.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2017-08-24 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR 2017-09-07 * "Hoogle" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hoogle:Salary -4615.38 USD Income:US:Hoogle:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2017:US:Medicare 106.62 USD Expenses:Taxes:Y2017:US:Federal 1062.92 USD Expenses:Taxes:Y2017:US:State 365.08 USD Expenses:Taxes:Y2017:US:CityNYC 174.92 USD Expenses:Taxes:Y2017:US:SDI 1.12 USD Expenses:Taxes:Y2017:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hoogle:Vacation 5 VACHR Income:US:Hoogle:Vacation -5 VACHR * Taxes 1980-05-12 open Income:US:Federal:PreTax401k IRAUSD 1980-05-12 open Assets:US:Federal:PreTax401k IRAUSD ** Tax Year 2015 2015-01-01 open Expenses:Taxes:Y2015:US:Federal:PreTax401k IRAUSD 2015-01-01 open Expenses:Taxes:Y2015:US:Medicare USD 2015-01-01 open Expenses:Taxes:Y2015:US:Federal USD 2015-01-01 open Expenses:Taxes:Y2015:US:CityNYC USD 2015-01-01 open Expenses:Taxes:Y2015:US:SDI USD 2015-01-01 open Expenses:Taxes:Y2015:US:State USD 2015-01-01 open Expenses:Taxes:Y2015:US:SocSec USD 2015-01-01 balance Assets:US:Federal:PreTax401k 0 IRAUSD 2015-01-01 * "Allowed contributions for one year" Income:US:Federal:PreTax401k -18000 IRAUSD Assets:US:Federal:PreTax401k 18000 IRAUSD 2016-03-20 * "Filing taxes for 2015" Expenses:Taxes:Y2015:US:Federal 295.23 USD Expenses:Taxes:Y2015:US:State 274.92 USD Liabilities:AccountsPayable -570.15 USD 2016-03-22 * "FEDERAL TAXPYMT" Assets:US:BofA:Checking -295.23 USD Liabilities:AccountsPayable 295.23 USD 2016-03-23 * "STATE TAX & FINANC PYMT" Assets:US:BofA:Checking -274.92 USD Liabilities:AccountsPayable 274.92 USD ** Tax Year 2016 2016-01-01 open Expenses:Taxes:Y2016:US:Federal:PreTax401k IRAUSD 2016-01-01 open Expenses:Taxes:Y2016:US:Medicare USD 2016-01-01 open Expenses:Taxes:Y2016:US:Federal USD 2016-01-01 open Expenses:Taxes:Y2016:US:CityNYC USD 2016-01-01 open Expenses:Taxes:Y2016:US:SDI USD 2016-01-01 open Expenses:Taxes:Y2016:US:State USD 2016-01-01 open Expenses:Taxes:Y2016:US:SocSec USD 2016-01-01 balance Assets:US:Federal:PreTax401k 0 IRAUSD 2016-01-01 * "Allowed contributions for one year" Income:US:Federal:PreTax401k -18000 IRAUSD Assets:US:Federal:PreTax401k 18000 IRAUSD 2017-03-21 * "Filing taxes for 2016" Expenses:Taxes:Y2016:US:Federal 463.16 USD Expenses:Taxes:Y2016:US:State 456.53 USD Liabilities:AccountsPayable -919.69 USD 2017-03-21 * "FEDERAL TAXPYMT" Assets:US:BofA:Checking -463.16 USD Liabilities:AccountsPayable 463.16 USD 2017-03-21 * "STATE TAX & FINANC PYMT" Assets:US:BofA:Checking -456.53 USD Liabilities:AccountsPayable 456.53 USD ** Tax Year 2017 2017-01-01 open Expenses:Taxes:Y2017:US:Federal:PreTax401k IRAUSD 2017-01-01 open Expenses:Taxes:Y2017:US:Medicare USD 2017-01-01 open Expenses:Taxes:Y2017:US:Federal USD 2017-01-01 open Expenses:Taxes:Y2017:US:CityNYC USD 2017-01-01 open Expenses:Taxes:Y2017:US:SDI USD 2017-01-01 open Expenses:Taxes:Y2017:US:State USD 2017-01-01 open Expenses:Taxes:Y2017:US:SocSec USD 2017-01-01 balance Assets:US:Federal:PreTax401k 0 IRAUSD 2017-01-01 * "Allowed contributions for one year" Income:US:Federal:PreTax401k -18500 IRAUSD Assets:US:Federal:PreTax401k 18500 IRAUSD * Expenses 1980-05-12 open Expenses:Food:Groceries 1980-05-12 open Expenses:Food:Restaurant 1980-05-12 open Expenses:Food:Coffee 1980-05-12 open Expenses:Food:Alcohol 1980-05-12 open Expenses:Transport:Tram 1980-05-12 open Expenses:Home:Rent 1980-05-12 open Expenses:Home:Electricity 1980-05-12 open Expenses:Home:Internet 1980-05-12 open Expenses:Home:Phone 1980-05-12 open Expenses:Financial:Fees 1980-05-12 open Expenses:Financial:Commissions * Prices 2015-01-02 price VBMPX 136.65 USD 2015-01-02 price RGAGX 88.07 USD 2015-01-02 price ITOT 54.33 USD 2015-01-02 price VEA 131.84 USD 2015-01-02 price VHT 109.95 USD 2015-01-02 price GLD 167.19 USD 2015-01-09 price VBMPX 136.57 USD 2015-01-09 price RGAGX 86.79 USD 2015-01-09 price ITOT 54.85 USD 2015-01-09 price VEA 132.10 USD 2015-01-09 price VHT 108.61 USD 2015-01-09 price GLD 168.02 USD 2015-01-16 price VBMPX 138.35 USD 2015-01-16 price RGAGX 88.87 USD 2015-01-16 price ITOT 55.36 USD 2015-01-16 price VEA 132.08 USD 2015-01-16 price VHT 108.85 USD 2015-01-16 price GLD 170.99 USD 2015-01-23 price VBMPX 138.47 USD 2015-01-23 price RGAGX 86.70 USD 2015-01-23 price ITOT 54.76 USD 2015-01-23 price VEA 131.60 USD 2015-01-23 price VHT 108.29 USD 2015-01-23 price GLD 175.46 USD 2015-01-30 price VBMPX 139.94 USD 2015-01-30 price RGAGX 87.85 USD 2015-01-30 price ITOT 55.76 USD 2015-01-30 price VEA 131.85 USD 2015-01-30 price VHT 105.64 USD 2015-01-30 price GLD 175.57 USD 2015-02-06 price VBMPX 140.56 USD 2015-02-06 price RGAGX 88.89 USD 2015-02-06 price ITOT 56.12 USD 2015-02-06 price VEA 131.69 USD 2015-02-06 price VHT 106.62 USD 2015-02-06 price GLD 177.17 USD 2015-02-13 price VBMPX 140.34 USD 2015-02-13 price RGAGX 88.22 USD 2015-02-13 price ITOT 57.24 USD 2015-02-13 price VEA 132.55 USD 2015-02-13 price VHT 105.63 USD 2015-02-13 price GLD 177.31 USD 2015-02-20 price VBMPX 141.59 USD 2015-02-20 price RGAGX 87.30 USD 2015-02-20 price ITOT 58.18 USD 2015-02-20 price VEA 130.77 USD 2015-02-20 price VHT 106.56 USD 2015-02-20 price GLD 179.46 USD 2015-02-27 price VBMPX 140.74 USD 2015-02-27 price RGAGX 86.62 USD 2015-02-27 price ITOT 58.61 USD 2015-02-27 price VEA 130.58 USD 2015-02-27 price VHT 107.37 USD 2015-02-27 price GLD 181.89 USD 2015-03-06 price VBMPX 142.35 USD 2015-03-06 price RGAGX 85.95 USD 2015-03-06 price ITOT 58.18 USD 2015-03-06 price VEA 130.19 USD 2015-03-06 price VHT 107.68 USD 2015-03-06 price GLD 186.07 USD 2015-03-13 price VBMPX 142.20 USD 2015-03-13 price RGAGX 86.15 USD 2015-03-13 price ITOT 58.51 USD 2015-03-13 price VEA 130.07 USD 2015-03-13 price VHT 107.56 USD 2015-03-13 price GLD 187.11 USD 2015-03-20 price VBMPX 142.87 USD 2015-03-20 price RGAGX 84.43 USD 2015-03-20 price ITOT 57.95 USD 2015-03-20 price VEA 131.52 USD 2015-03-20 price VHT 109.29 USD 2015-03-20 price GLD 188.69 USD 2015-03-27 price VBMPX 141.48 USD 2015-03-27 price RGAGX 84.63 USD 2015-03-27 price ITOT 58.35 USD 2015-03-27 price VEA 132.78 USD 2015-03-27 price VHT 111.59 USD 2015-03-27 price GLD 187.15 USD 2015-04-03 price VBMPX 142.03 USD 2015-04-03 price RGAGX 83.85 USD 2015-04-03 price ITOT 57.56 USD 2015-04-03 price VEA 135.81 USD 2015-04-03 price VHT 110.28 USD 2015-04-03 price GLD 192.47 USD 2015-04-10 price VBMPX 143.57 USD 2015-04-10 price RGAGX 83.67 USD 2015-04-10 price ITOT 57.37 USD 2015-04-10 price VEA 136.01 USD 2015-04-10 price VHT 108.59 USD 2015-04-10 price GLD 198.11 USD 2015-04-17 price VBMPX 144.49 USD 2015-04-17 price RGAGX 81.89 USD 2015-04-17 price ITOT 57.90 USD 2015-04-17 price VEA 134.09 USD 2015-04-17 price VHT 108.86 USD 2015-04-17 price GLD 198.35 USD 2015-04-24 price VBMPX 145.00 USD 2015-04-24 price RGAGX 82.26 USD 2015-04-24 price ITOT 57.63 USD 2015-04-24 price VEA 133.87 USD 2015-04-24 price VHT 109.64 USD 2015-04-24 price GLD 196.68 USD 2015-05-01 price VBMPX 145.64 USD 2015-05-01 price RGAGX 80.03 USD 2015-05-01 price ITOT 56.47 USD 2015-05-01 price VEA 134.30 USD 2015-05-01 price VHT 107.78 USD 2015-05-01 price GLD 198.74 USD 2015-05-08 price VBMPX 147.63 USD 2015-05-08 price RGAGX 79.16 USD 2015-05-08 price ITOT 55.88 USD 2015-05-08 price VEA 136.37 USD 2015-05-08 price VHT 107.49 USD 2015-05-08 price GLD 195.85 USD 2015-05-15 price VBMPX 147.44 USD 2015-05-15 price RGAGX 77.98 USD 2015-05-15 price ITOT 56.51 USD 2015-05-15 price VEA 134.34 USD 2015-05-15 price VHT 106.52 USD 2015-05-15 price GLD 193.80 USD 2015-05-22 price VBMPX 148.55 USD 2015-05-22 price RGAGX 77.08 USD 2015-05-22 price ITOT 56.78 USD 2015-05-22 price VEA 133.32 USD 2015-05-22 price VHT 109.33 USD 2015-05-22 price GLD 194.35 USD 2015-05-29 price VBMPX 147.40 USD 2015-05-29 price RGAGX 76.92 USD 2015-05-29 price ITOT 56.81 USD 2015-05-29 price VEA 132.88 USD 2015-05-29 price VHT 109.68 USD 2015-05-29 price GLD 198.00 USD 2015-06-05 price VBMPX 148.91 USD 2015-06-05 price RGAGX 76.41 USD 2015-06-05 price ITOT 57.43 USD 2015-06-05 price VEA 133.29 USD 2015-06-05 price VHT 109.99 USD 2015-06-05 price GLD 194.57 USD 2015-06-12 price VBMPX 148.76 USD 2015-06-12 price RGAGX 75.62 USD 2015-06-12 price ITOT 58.43 USD 2015-06-12 price VEA 131.69 USD 2015-06-12 price VHT 112.10 USD 2015-06-12 price GLD 195.26 USD 2015-06-19 price VBMPX 150.01 USD 2015-06-19 price RGAGX 76.25 USD 2015-06-19 price ITOT 57.55 USD 2015-06-19 price VEA 130.25 USD 2015-06-19 price VHT 110.73 USD 2015-06-19 price GLD 194.54 USD 2015-06-26 price VBMPX 149.01 USD 2015-06-26 price RGAGX 76.78 USD 2015-06-26 price ITOT 58.59 USD 2015-06-26 price VEA 131.55 USD 2015-06-26 price VHT 110.52 USD 2015-06-26 price GLD 193.14 USD 2015-07-03 price VBMPX 148.80 USD 2015-07-03 price RGAGX 76.73 USD 2015-07-03 price ITOT 58.16 USD 2015-07-03 price VEA 131.38 USD 2015-07-03 price VHT 111.38 USD 2015-07-03 price GLD 190.84 USD 2015-07-10 price VBMPX 151.29 USD 2015-07-10 price RGAGX 76.71 USD 2015-07-10 price ITOT 59.25 USD 2015-07-10 price VEA 130.75 USD 2015-07-10 price VHT 111.71 USD 2015-07-10 price GLD 187.60 USD 2015-07-17 price VBMPX 151.23 USD 2015-07-17 price RGAGX 76.93 USD 2015-07-17 price ITOT 58.98 USD 2015-07-17 price VEA 130.81 USD 2015-07-17 price VHT 112.72 USD 2015-07-17 price GLD 187.89 USD 2015-07-24 price VBMPX 150.56 USD 2015-07-24 price RGAGX 78.58 USD 2015-07-24 price ITOT 59.23 USD 2015-07-24 price VEA 130.18 USD 2015-07-24 price VHT 113.00 USD 2015-07-24 price GLD 191.51 USD 2015-07-31 price VBMPX 152.83 USD 2015-07-31 price RGAGX 79.24 USD 2015-07-31 price ITOT 59.08 USD 2015-07-31 price VEA 131.90 USD 2015-07-31 price VHT 112.79 USD 2015-07-31 price GLD 192.22 USD 2015-08-07 price VBMPX 153.76 USD 2015-08-07 price RGAGX 80.74 USD 2015-08-07 price ITOT 60.20 USD 2015-08-07 price VEA 133.22 USD 2015-08-07 price VHT 112.41 USD 2015-08-07 price GLD 193.48 USD 2015-08-14 price VBMPX 154.48 USD 2015-08-14 price RGAGX 79.48 USD 2015-08-14 price ITOT 60.73 USD 2015-08-14 price VEA 133.58 USD 2015-08-14 price VHT 113.84 USD 2015-08-14 price GLD 196.92 USD 2015-08-21 price VBMPX 153.36 USD 2015-08-21 price RGAGX 78.86 USD 2015-08-21 price ITOT 60.25 USD 2015-08-21 price VEA 132.89 USD 2015-08-21 price VHT 114.10 USD 2015-08-21 price GLD 192.06 USD 2015-08-28 price VBMPX 152.66 USD 2015-08-28 price RGAGX 78.73 USD 2015-08-28 price ITOT 59.16 USD 2015-08-28 price VEA 133.28 USD 2015-08-28 price VHT 113.24 USD 2015-08-28 price GLD 191.74 USD 2015-09-04 price VBMPX 153.94 USD 2015-09-04 price RGAGX 77.67 USD 2015-09-04 price ITOT 59.05 USD 2015-09-04 price VEA 133.75 USD 2015-09-04 price VHT 112.69 USD 2015-09-04 price GLD 195.02 USD 2015-09-11 price VBMPX 155.72 USD 2015-09-11 price RGAGX 77.71 USD 2015-09-11 price ITOT 58.86 USD 2015-09-11 price VEA 132.93 USD 2015-09-11 price VHT 113.91 USD 2015-09-11 price GLD 197.06 USD 2015-09-18 price VBMPX 155.69 USD 2015-09-18 price RGAGX 77.93 USD 2015-09-18 price ITOT 59.20 USD 2015-09-18 price VEA 133.06 USD 2015-09-18 price VHT 114.59 USD 2015-09-18 price GLD 200.22 USD 2015-09-25 price VBMPX 156.09 USD 2015-09-25 price RGAGX 78.08 USD 2015-09-25 price ITOT 59.62 USD 2015-09-25 price VEA 134.33 USD 2015-09-25 price VHT 113.69 USD 2015-09-25 price GLD 201.88 USD 2015-10-02 price VBMPX 154.62 USD 2015-10-02 price RGAGX 76.91 USD 2015-10-02 price ITOT 60.02 USD 2015-10-02 price VEA 134.24 USD 2015-10-02 price VHT 115.52 USD 2015-10-02 price GLD 201.36 USD 2015-10-09 price VBMPX 154.57 USD 2015-10-09 price RGAGX 76.98 USD 2015-10-09 price ITOT 60.06 USD 2015-10-09 price VEA 135.78 USD 2015-10-09 price VHT 113.36 USD 2015-10-09 price GLD 202.82 USD 2015-10-16 price VBMPX 156.30 USD 2015-10-16 price RGAGX 78.33 USD 2015-10-16 price ITOT 60.50 USD 2015-10-16 price VEA 137.57 USD 2015-10-16 price VHT 114.77 USD 2015-10-16 price GLD 199.86 USD 2015-10-23 price VBMPX 157.17 USD 2015-10-23 price RGAGX 78.33 USD 2015-10-23 price ITOT 61.31 USD 2015-10-23 price VEA 136.68 USD 2015-10-23 price VHT 115.91 USD 2015-10-23 price GLD 197.74 USD 2015-10-30 price VBMPX 157.54 USD 2015-10-30 price RGAGX 77.58 USD 2015-10-30 price ITOT 61.54 USD 2015-10-30 price VEA 137.01 USD 2015-10-30 price VHT 113.32 USD 2015-10-30 price GLD 204.23 USD 2015-11-06 price VBMPX 158.75 USD 2015-11-06 price RGAGX 78.58 USD 2015-11-06 price ITOT 60.94 USD 2015-11-06 price VEA 136.52 USD 2015-11-06 price VHT 113.64 USD 2015-11-06 price GLD 205.12 USD 2015-11-13 price VBMPX 159.36 USD 2015-11-13 price RGAGX 79.22 USD 2015-11-13 price ITOT 61.74 USD 2015-11-13 price VEA 137.15 USD 2015-11-13 price VHT 112.21 USD 2015-11-13 price GLD 209.00 USD 2015-11-20 price VBMPX 161.60 USD 2015-11-20 price RGAGX 79.07 USD 2015-11-20 price ITOT 60.82 USD 2015-11-20 price VEA 136.79 USD 2015-11-20 price VHT 113.21 USD 2015-11-20 price GLD 204.30 USD 2015-11-27 price VBMPX 162.26 USD 2015-11-27 price RGAGX 79.43 USD 2015-11-27 price ITOT 60.75 USD 2015-11-27 price VEA 137.92 USD 2015-11-27 price VHT 116.31 USD 2015-11-27 price GLD 208.25 USD 2015-12-04 price VBMPX 162.66 USD 2015-12-04 price RGAGX 80.38 USD 2015-12-04 price ITOT 59.69 USD 2015-12-04 price VEA 139.43 USD 2015-12-04 price VHT 116.15 USD 2015-12-04 price GLD 207.05 USD 2015-12-11 price VBMPX 162.12 USD 2015-12-11 price RGAGX 79.91 USD 2015-12-11 price ITOT 60.93 USD 2015-12-11 price VEA 137.60 USD 2015-12-11 price VHT 115.91 USD 2015-12-11 price GLD 204.46 USD 2015-12-18 price VBMPX 163.27 USD 2015-12-18 price RGAGX 80.92 USD 2015-12-18 price ITOT 61.32 USD 2015-12-18 price VEA 137.50 USD 2015-12-18 price VHT 114.01 USD 2015-12-18 price GLD 200.21 USD 2015-12-25 price VBMPX 163.17 USD 2015-12-25 price RGAGX 79.58 USD 2015-12-25 price ITOT 61.34 USD 2015-12-25 price VEA 136.96 USD 2015-12-25 price VHT 112.21 USD 2015-12-25 price GLD 202.96 USD 2016-01-01 price VBMPX 165.77 USD 2016-01-01 price RGAGX 78.97 USD 2016-01-01 price ITOT 61.40 USD 2016-01-01 price VEA 137.09 USD 2016-01-01 price VHT 112.27 USD 2016-01-01 price GLD 200.56 USD 2016-01-08 price VBMPX 165.05 USD 2016-01-08 price RGAGX 78.41 USD 2016-01-08 price ITOT 61.04 USD 2016-01-08 price VEA 138.58 USD 2016-01-08 price VHT 111.37 USD 2016-01-08 price GLD 198.06 USD 2016-01-15 price VBMPX 166.33 USD 2016-01-15 price RGAGX 79.50 USD 2016-01-15 price ITOT 60.38 USD 2016-01-15 price VEA 137.19 USD 2016-01-15 price VHT 111.88 USD 2016-01-15 price GLD 200.23 USD 2016-01-22 price VBMPX 167.66 USD 2016-01-22 price RGAGX 78.56 USD 2016-01-22 price ITOT 60.53 USD 2016-01-22 price VEA 136.21 USD 2016-01-22 price VHT 112.88 USD 2016-01-22 price GLD 198.57 USD 2016-01-29 price VBMPX 168.90 USD 2016-01-29 price RGAGX 79.55 USD 2016-01-29 price ITOT 61.08 USD 2016-01-29 price VEA 138.57 USD 2016-01-29 price VHT 114.11 USD 2016-01-29 price GLD 199.55 USD 2016-02-05 price VBMPX 169.45 USD 2016-02-05 price RGAGX 79.52 USD 2016-02-05 price ITOT 61.83 USD 2016-02-05 price VEA 139.41 USD 2016-02-05 price VHT 112.46 USD 2016-02-05 price GLD 200.43 USD 2016-02-12 price VBMPX 168.94 USD 2016-02-12 price RGAGX 79.04 USD 2016-02-12 price ITOT 63.22 USD 2016-02-12 price VEA 137.55 USD 2016-02-12 price VHT 112.63 USD 2016-02-12 price GLD 198.33 USD 2016-02-19 price VBMPX 169.02 USD 2016-02-19 price RGAGX 78.06 USD 2016-02-19 price ITOT 62.63 USD 2016-02-19 price VEA 137.49 USD 2016-02-19 price VHT 114.40 USD 2016-02-19 price GLD 197.43 USD 2016-02-26 price VBMPX 169.50 USD 2016-02-26 price RGAGX 78.44 USD 2016-02-26 price ITOT 62.62 USD 2016-02-26 price VEA 136.78 USD 2016-02-26 price VHT 115.01 USD 2016-02-26 price GLD 201.40 USD 2016-03-04 price VBMPX 171.38 USD 2016-03-04 price RGAGX 78.40 USD 2016-03-04 price ITOT 62.47 USD 2016-03-04 price VEA 136.52 USD 2016-03-04 price VHT 113.52 USD 2016-03-04 price GLD 201.13 USD 2016-03-11 price VBMPX 172.43 USD 2016-03-11 price RGAGX 79.41 USD 2016-03-11 price ITOT 62.64 USD 2016-03-11 price VEA 136.50 USD 2016-03-11 price VHT 113.41 USD 2016-03-11 price GLD 204.95 USD 2016-03-18 price VBMPX 172.55 USD 2016-03-18 price RGAGX 80.91 USD 2016-03-18 price ITOT 62.64 USD 2016-03-18 price VEA 136.67 USD 2016-03-18 price VHT 113.56 USD 2016-03-18 price GLD 202.13 USD 2016-03-25 price VBMPX 173.31 USD 2016-03-25 price RGAGX 80.91 USD 2016-03-25 price ITOT 62.99 USD 2016-03-25 price VEA 134.62 USD 2016-03-25 price VHT 114.97 USD 2016-03-25 price GLD 204.01 USD 2016-04-01 price VBMPX 173.77 USD 2016-04-01 price RGAGX 79.87 USD 2016-04-01 price ITOT 62.66 USD 2016-04-01 price VEA 133.85 USD 2016-04-01 price VHT 116.46 USD 2016-04-01 price GLD 203.61 USD 2016-04-08 price VBMPX 173.94 USD 2016-04-08 price RGAGX 81.72 USD 2016-04-08 price ITOT 62.38 USD 2016-04-08 price VEA 134.56 USD 2016-04-08 price VHT 116.45 USD 2016-04-08 price GLD 208.51 USD 2016-04-15 price VBMPX 175.06 USD 2016-04-15 price RGAGX 82.58 USD 2016-04-15 price ITOT 61.69 USD 2016-04-15 price VEA 133.84 USD 2016-04-15 price VHT 120.00 USD 2016-04-15 price GLD 210.80 USD 2016-04-22 price VBMPX 178.22 USD 2016-04-22 price RGAGX 81.20 USD 2016-04-22 price ITOT 61.80 USD 2016-04-22 price VEA 135.08 USD 2016-04-22 price VHT 119.64 USD 2016-04-22 price GLD 204.82 USD 2016-04-29 price VBMPX 176.69 USD 2016-04-29 price RGAGX 82.36 USD 2016-04-29 price ITOT 62.20 USD 2016-04-29 price VEA 134.63 USD 2016-04-29 price VHT 118.03 USD 2016-04-29 price GLD 205.78 USD 2016-05-06 price VBMPX 176.67 USD 2016-05-06 price RGAGX 82.22 USD 2016-05-06 price ITOT 63.22 USD 2016-05-06 price VEA 137.35 USD 2016-05-06 price VHT 118.13 USD 2016-05-06 price GLD 210.48 USD 2016-05-13 price VBMPX 175.04 USD 2016-05-13 price RGAGX 82.99 USD 2016-05-13 price ITOT 63.43 USD 2016-05-13 price VEA 138.95 USD 2016-05-13 price VHT 118.53 USD 2016-05-13 price GLD 210.99 USD 2016-05-20 price VBMPX 177.15 USD 2016-05-20 price RGAGX 83.38 USD 2016-05-20 price ITOT 63.32 USD 2016-05-20 price VEA 136.59 USD 2016-05-20 price VHT 118.62 USD 2016-05-20 price GLD 210.97 USD 2016-05-27 price VBMPX 178.27 USD 2016-05-27 price RGAGX 84.89 USD 2016-05-27 price ITOT 63.86 USD 2016-05-27 price VEA 135.41 USD 2016-05-27 price VHT 116.93 USD 2016-05-27 price GLD 214.77 USD 2016-06-03 price VBMPX 178.72 USD 2016-06-03 price RGAGX 85.28 USD 2016-06-03 price ITOT 63.97 USD 2016-06-03 price VEA 135.53 USD 2016-06-03 price VHT 115.82 USD 2016-06-03 price GLD 217.26 USD 2016-06-10 price VBMPX 178.65 USD 2016-06-10 price RGAGX 85.05 USD 2016-06-10 price ITOT 64.04 USD 2016-06-10 price VEA 134.45 USD 2016-06-10 price VHT 116.27 USD 2016-06-10 price GLD 215.52 USD 2016-06-17 price VBMPX 180.41 USD 2016-06-17 price RGAGX 85.62 USD 2016-06-17 price ITOT 63.94 USD 2016-06-17 price VEA 133.32 USD 2016-06-17 price VHT 116.21 USD 2016-06-17 price GLD 221.83 USD 2016-06-24 price VBMPX 180.21 USD 2016-06-24 price RGAGX 85.48 USD 2016-06-24 price ITOT 63.83 USD 2016-06-24 price VEA 135.58 USD 2016-06-24 price VHT 117.22 USD 2016-06-24 price GLD 220.27 USD 2016-07-01 price VBMPX 180.86 USD 2016-07-01 price RGAGX 85.66 USD 2016-07-01 price ITOT 64.44 USD 2016-07-01 price VEA 135.78 USD 2016-07-01 price VHT 116.23 USD 2016-07-01 price GLD 221.08 USD 2016-07-08 price VBMPX 180.12 USD 2016-07-08 price RGAGX 84.05 USD 2016-07-08 price ITOT 63.97 USD 2016-07-08 price VEA 136.00 USD 2016-07-08 price VHT 115.78 USD 2016-07-08 price GLD 226.85 USD 2016-07-15 price VBMPX 180.75 USD 2016-07-15 price RGAGX 82.52 USD 2016-07-15 price ITOT 64.69 USD 2016-07-15 price VEA 137.14 USD 2016-07-15 price VHT 117.19 USD 2016-07-15 price GLD 228.39 USD 2016-07-22 price VBMPX 181.69 USD 2016-07-22 price RGAGX 82.53 USD 2016-07-22 price ITOT 66.04 USD 2016-07-22 price VEA 138.58 USD 2016-07-22 price VHT 118.19 USD 2016-07-22 price GLD 227.01 USD 2016-07-29 price VBMPX 182.73 USD 2016-07-29 price RGAGX 84.29 USD 2016-07-29 price ITOT 65.88 USD 2016-07-29 price VEA 140.25 USD 2016-07-29 price VHT 117.99 USD 2016-07-29 price GLD 225.49 USD 2016-08-05 price VBMPX 182.35 USD 2016-08-05 price RGAGX 84.81 USD 2016-08-05 price ITOT 65.90 USD 2016-08-05 price VEA 139.48 USD 2016-08-05 price VHT 118.60 USD 2016-08-05 price GLD 224.35 USD 2016-08-12 price VBMPX 183.91 USD 2016-08-12 price RGAGX 83.77 USD 2016-08-12 price ITOT 66.51 USD 2016-08-12 price VEA 140.97 USD 2016-08-12 price VHT 119.85 USD 2016-08-12 price GLD 225.99 USD 2016-08-19 price VBMPX 183.99 USD 2016-08-19 price RGAGX 83.58 USD 2016-08-19 price ITOT 66.40 USD 2016-08-19 price VEA 141.28 USD 2016-08-19 price VHT 119.56 USD 2016-08-19 price GLD 226.87 USD 2016-08-26 price VBMPX 183.77 USD 2016-08-26 price RGAGX 82.67 USD 2016-08-26 price ITOT 65.49 USD 2016-08-26 price VEA 141.11 USD 2016-08-26 price VHT 118.08 USD 2016-08-26 price GLD 221.58 USD 2016-09-02 price VBMPX 183.92 USD 2016-09-02 price RGAGX 82.90 USD 2016-09-02 price ITOT 65.77 USD 2016-09-02 price VEA 141.92 USD 2016-09-02 price VHT 119.80 USD 2016-09-02 price GLD 223.34 USD 2016-09-09 price VBMPX 183.15 USD 2016-09-09 price RGAGX 81.96 USD 2016-09-09 price ITOT 65.89 USD 2016-09-09 price VEA 141.59 USD 2016-09-09 price VHT 120.64 USD 2016-09-09 price GLD 225.70 USD 2016-09-16 price VBMPX 183.42 USD 2016-09-16 price RGAGX 81.05 USD 2016-09-16 price ITOT 65.97 USD 2016-09-16 price VEA 143.89 USD 2016-09-16 price VHT 119.82 USD 2016-09-16 price GLD 230.62 USD 2016-09-23 price VBMPX 181.93 USD 2016-09-23 price RGAGX 80.59 USD 2016-09-23 price ITOT 65.47 USD 2016-09-23 price VEA 144.87 USD 2016-09-23 price VHT 119.17 USD 2016-09-23 price GLD 231.38 USD 2016-09-30 price VBMPX 182.66 USD 2016-09-30 price RGAGX 80.13 USD 2016-09-30 price ITOT 65.21 USD 2016-09-30 price VEA 144.02 USD 2016-09-30 price VHT 118.94 USD 2016-09-30 price GLD 232.95 USD 2016-10-07 price VBMPX 183.07 USD 2016-10-07 price RGAGX 80.02 USD 2016-10-07 price ITOT 65.31 USD 2016-10-07 price VEA 145.05 USD 2016-10-07 price VHT 118.78 USD 2016-10-07 price GLD 232.81 USD 2016-10-14 price VBMPX 183.31 USD 2016-10-14 price RGAGX 79.50 USD 2016-10-14 price ITOT 65.07 USD 2016-10-14 price VEA 146.32 USD 2016-10-14 price VHT 118.60 USD 2016-10-14 price GLD 233.50 USD 2016-10-21 price VBMPX 181.35 USD 2016-10-21 price RGAGX 80.79 USD 2016-10-21 price ITOT 65.35 USD 2016-10-21 price VEA 148.45 USD 2016-10-21 price VHT 119.95 USD 2016-10-21 price GLD 234.91 USD 2016-10-28 price VBMPX 182.89 USD 2016-10-28 price RGAGX 80.44 USD 2016-10-28 price ITOT 65.67 USD 2016-10-28 price VEA 149.06 USD 2016-10-28 price VHT 118.95 USD 2016-10-28 price GLD 238.91 USD 2016-11-04 price VBMPX 182.69 USD 2016-11-04 price RGAGX 80.36 USD 2016-11-04 price ITOT 65.54 USD 2016-11-04 price VEA 149.75 USD 2016-11-04 price VHT 118.35 USD 2016-11-04 price GLD 238.35 USD 2016-11-11 price VBMPX 182.51 USD 2016-11-11 price RGAGX 81.39 USD 2016-11-11 price ITOT 65.41 USD 2016-11-11 price VEA 147.45 USD 2016-11-11 price VHT 117.36 USD 2016-11-11 price GLD 236.31 USD 2016-11-18 price VBMPX 181.47 USD 2016-11-18 price RGAGX 81.84 USD 2016-11-18 price ITOT 67.07 USD 2016-11-18 price VEA 145.37 USD 2016-11-18 price VHT 118.31 USD 2016-11-18 price GLD 240.51 USD 2016-11-25 price VBMPX 182.02 USD 2016-11-25 price RGAGX 82.74 USD 2016-11-25 price ITOT 67.49 USD 2016-11-25 price VEA 143.62 USD 2016-11-25 price VHT 117.41 USD 2016-11-25 price GLD 241.49 USD 2016-12-02 price VBMPX 182.43 USD 2016-12-02 price RGAGX 83.80 USD 2016-12-02 price ITOT 67.02 USD 2016-12-02 price VEA 144.25 USD 2016-12-02 price VHT 116.80 USD 2016-12-02 price GLD 247.85 USD 2016-12-09 price VBMPX 182.79 USD 2016-12-09 price RGAGX 81.98 USD 2016-12-09 price ITOT 67.13 USD 2016-12-09 price VEA 147.58 USD 2016-12-09 price VHT 118.49 USD 2016-12-09 price GLD 250.31 USD 2016-12-16 price VBMPX 181.33 USD 2016-12-16 price RGAGX 81.53 USD 2016-12-16 price ITOT 68.40 USD 2016-12-16 price VEA 146.13 USD 2016-12-16 price VHT 116.69 USD 2016-12-16 price GLD 251.47 USD 2016-12-23 price VBMPX 180.01 USD 2016-12-23 price RGAGX 81.08 USD 2016-12-23 price ITOT 68.26 USD 2016-12-23 price VEA 145.03 USD 2016-12-23 price VHT 116.33 USD 2016-12-23 price GLD 255.18 USD 2016-12-30 price VBMPX 179.51 USD 2016-12-30 price RGAGX 81.93 USD 2016-12-30 price ITOT 68.60 USD 2016-12-30 price VEA 146.17 USD 2016-12-30 price VHT 115.62 USD 2016-12-30 price GLD 252.86 USD 2017-01-06 price VBMPX 180.52 USD 2017-01-06 price RGAGX 81.81 USD 2017-01-06 price ITOT 70.10 USD 2017-01-06 price VEA 146.44 USD 2017-01-06 price VHT 115.78 USD 2017-01-06 price GLD 261.96 USD 2017-01-13 price VBMPX 179.68 USD 2017-01-13 price RGAGX 80.98 USD 2017-01-13 price ITOT 70.86 USD 2017-01-13 price VEA 146.83 USD 2017-01-13 price VHT 116.13 USD 2017-01-13 price GLD 262.46 USD 2017-01-20 price VBMPX 180.67 USD 2017-01-20 price RGAGX 81.44 USD 2017-01-20 price ITOT 69.84 USD 2017-01-20 price VEA 147.73 USD 2017-01-20 price VHT 113.39 USD 2017-01-20 price GLD 261.56 USD 2017-01-27 price VBMPX 181.60 USD 2017-01-27 price RGAGX 81.13 USD 2017-01-27 price ITOT 70.25 USD 2017-01-27 price VEA 149.07 USD 2017-01-27 price VHT 115.60 USD 2017-01-27 price GLD 263.28 USD 2017-02-03 price VBMPX 183.80 USD 2017-02-03 price RGAGX 81.69 USD 2017-02-03 price ITOT 71.00 USD 2017-02-03 price VEA 147.37 USD 2017-02-03 price VHT 114.68 USD 2017-02-03 price GLD 266.80 USD 2017-02-10 price VBMPX 182.75 USD 2017-02-10 price RGAGX 82.15 USD 2017-02-10 price ITOT 71.74 USD 2017-02-10 price VEA 147.94 USD 2017-02-10 price VHT 114.54 USD 2017-02-10 price GLD 273.28 USD 2017-02-17 price VBMPX 184.02 USD 2017-02-17 price RGAGX 81.90 USD 2017-02-17 price ITOT 72.96 USD 2017-02-17 price VEA 149.80 USD 2017-02-17 price VHT 114.15 USD 2017-02-17 price GLD 278.87 USD 2017-02-24 price VBMPX 185.73 USD 2017-02-24 price RGAGX 81.24 USD 2017-02-24 price ITOT 72.89 USD 2017-02-24 price VEA 149.22 USD 2017-02-24 price VHT 111.84 USD 2017-02-24 price GLD 280.47 USD 2017-03-03 price VBMPX 185.30 USD 2017-03-03 price RGAGX 81.31 USD 2017-03-03 price ITOT 72.40 USD 2017-03-03 price VEA 148.21 USD 2017-03-03 price VHT 110.95 USD 2017-03-03 price GLD 276.79 USD 2017-03-10 price VBMPX 188.46 USD 2017-03-10 price RGAGX 80.12 USD 2017-03-10 price ITOT 73.87 USD 2017-03-10 price VEA 146.92 USD 2017-03-10 price VHT 110.17 USD 2017-03-10 price GLD 280.16 USD 2017-03-17 price VBMPX 189.50 USD 2017-03-17 price RGAGX 80.59 USD 2017-03-17 price ITOT 74.93 USD 2017-03-17 price VEA 148.59 USD 2017-03-17 price VHT 115.55 USD 2017-03-17 price GLD 278.99 USD 2017-03-24 price VBMPX 189.47 USD 2017-03-24 price RGAGX 81.16 USD 2017-03-24 price ITOT 75.83 USD 2017-03-24 price VEA 147.79 USD 2017-03-24 price VHT 116.73 USD 2017-03-24 price GLD 278.57 USD 2017-03-31 price VBMPX 190.07 USD 2017-03-31 price RGAGX 81.15 USD 2017-03-31 price ITOT 76.16 USD 2017-03-31 price VEA 146.80 USD 2017-03-31 price VHT 117.01 USD 2017-03-31 price GLD 283.02 USD 2017-04-07 price VBMPX 191.16 USD 2017-04-07 price RGAGX 80.46 USD 2017-04-07 price ITOT 76.60 USD 2017-04-07 price VEA 147.77 USD 2017-04-07 price VHT 118.93 USD 2017-04-07 price GLD 293.54 USD 2017-04-14 price VBMPX 195.13 USD 2017-04-14 price RGAGX 81.21 USD 2017-04-14 price ITOT 77.22 USD 2017-04-14 price VEA 149.67 USD 2017-04-14 price VHT 119.84 USD 2017-04-14 price GLD 293.31 USD 2017-04-21 price VBMPX 196.50 USD 2017-04-21 price RGAGX 80.45 USD 2017-04-21 price ITOT 78.37 USD 2017-04-21 price VEA 146.92 USD 2017-04-21 price VHT 119.10 USD 2017-04-21 price GLD 295.14 USD 2017-04-28 price VBMPX 198.28 USD 2017-04-28 price RGAGX 81.23 USD 2017-04-28 price ITOT 79.12 USD 2017-04-28 price VEA 146.79 USD 2017-04-28 price VHT 118.41 USD 2017-04-28 price GLD 299.07 USD 2017-05-05 price VBMPX 200.23 USD 2017-05-05 price RGAGX 81.57 USD 2017-05-05 price ITOT 79.27 USD 2017-05-05 price VEA 144.33 USD 2017-05-05 price VHT 119.35 USD 2017-05-05 price GLD 303.74 USD 2017-05-12 price VBMPX 200.90 USD 2017-05-12 price RGAGX 81.41 USD 2017-05-12 price ITOT 80.24 USD 2017-05-12 price VEA 144.48 USD 2017-05-12 price VHT 120.51 USD 2017-05-12 price GLD 300.63 USD 2017-05-19 price VBMPX 203.04 USD 2017-05-19 price RGAGX 82.51 USD 2017-05-19 price ITOT 80.49 USD 2017-05-19 price VEA 144.59 USD 2017-05-19 price VHT 120.10 USD 2017-05-19 price GLD 297.70 USD 2017-05-26 price VBMPX 203.92 USD 2017-05-26 price RGAGX 82.03 USD 2017-05-26 price ITOT 79.99 USD 2017-05-26 price VEA 146.96 USD 2017-05-26 price VHT 119.10 USD 2017-05-26 price GLD 297.03 USD 2017-06-02 price VBMPX 206.64 USD 2017-06-02 price RGAGX 82.12 USD 2017-06-02 price ITOT 80.16 USD 2017-06-02 price VEA 146.84 USD 2017-06-02 price VHT 121.63 USD 2017-06-02 price GLD 300.02 USD 2017-06-09 price VBMPX 205.92 USD 2017-06-09 price RGAGX 82.98 USD 2017-06-09 price ITOT 79.43 USD 2017-06-09 price VEA 146.73 USD 2017-06-09 price VHT 122.51 USD 2017-06-09 price GLD 299.92 USD 2017-06-16 price VBMPX 206.18 USD 2017-06-16 price RGAGX 84.40 USD 2017-06-16 price ITOT 78.76 USD 2017-06-16 price VEA 146.27 USD 2017-06-16 price VHT 121.28 USD 2017-06-16 price GLD 297.22 USD 2017-06-23 price VBMPX 206.98 USD 2017-06-23 price RGAGX 85.54 USD 2017-06-23 price ITOT 79.35 USD 2017-06-23 price VEA 146.25 USD 2017-06-23 price VHT 121.33 USD 2017-06-23 price GLD 298.01 USD 2017-06-30 price VBMPX 207.83 USD 2017-06-30 price RGAGX 84.98 USD 2017-06-30 price ITOT 79.41 USD 2017-06-30 price VEA 144.99 USD 2017-06-30 price VHT 122.85 USD 2017-06-30 price GLD 296.98 USD 2017-07-07 price VBMPX 208.45 USD 2017-07-07 price RGAGX 85.48 USD 2017-07-07 price ITOT 79.73 USD 2017-07-07 price VEA 145.49 USD 2017-07-07 price VHT 123.00 USD 2017-07-07 price GLD 291.25 USD 2017-07-14 price VBMPX 207.28 USD 2017-07-14 price RGAGX 84.41 USD 2017-07-14 price ITOT 80.20 USD 2017-07-14 price VEA 146.87 USD 2017-07-14 price VHT 125.01 USD 2017-07-14 price GLD 280.44 USD 2017-07-21 price VBMPX 208.34 USD 2017-07-21 price RGAGX 83.41 USD 2017-07-21 price ITOT 79.80 USD 2017-07-21 price VEA 143.85 USD 2017-07-21 price VHT 127.63 USD 2017-07-21 price GLD 289.83 USD 2017-07-28 price VBMPX 207.06 USD 2017-07-28 price RGAGX 83.30 USD 2017-07-28 price ITOT 80.56 USD 2017-07-28 price VEA 144.41 USD 2017-07-28 price VHT 130.92 USD 2017-07-28 price GLD 291.02 USD 2017-08-04 price VBMPX 207.40 USD 2017-08-04 price RGAGX 82.78 USD 2017-08-04 price ITOT 80.52 USD 2017-08-04 price VEA 145.97 USD 2017-08-04 price VHT 129.79 USD 2017-08-04 price GLD 297.80 USD 2017-08-11 price VBMPX 208.79 USD 2017-08-11 price RGAGX 81.40 USD 2017-08-11 price ITOT 81.02 USD 2017-08-11 price VEA 147.13 USD 2017-08-11 price VHT 131.26 USD 2017-08-11 price GLD 301.22 USD 2017-08-18 price VBMPX 210.22 USD 2017-08-18 price RGAGX 81.76 USD 2017-08-18 price ITOT 80.94 USD 2017-08-18 price VEA 145.72 USD 2017-08-18 price VHT 132.26 USD 2017-08-18 price GLD 302.94 USD 2017-08-25 price VBMPX 211.04 USD 2017-08-25 price RGAGX 81.74 USD 2017-08-25 price ITOT 80.68 USD 2017-08-25 price VEA 144.23 USD 2017-08-25 price VHT 132.32 USD 2017-08-25 price GLD 304.17 USD 2017-09-01 price VBMPX 211.69 USD 2017-09-01 price RGAGX 80.95 USD 2017-09-01 price ITOT 80.92 USD 2017-09-01 price VEA 142.80 USD 2017-09-01 price VHT 131.91 USD 2017-09-01 price GLD 304.48 USD 2017-09-08 price VBMPX 212.88 USD 2017-09-08 price RGAGX 81.09 USD 2017-09-08 price ITOT 79.93 USD 2017-09-08 price VEA 143.87 USD 2017-09-08 price VHT 130.97 USD 2017-09-08 price GLD 306.91 USD * Cash ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/contrib/examples/huge-example.beancount0000644000175000001440000336550600000000000022403 0ustar00jakobusers00000000000000;; -*- mode: org; mode: beancount; -*- ;; Birth: 1980-05-12 ;; Dates: 2008-01-01 - 2015-10-12 ;; THIS FILE HAS BEEN AUTO-GENERATED. * Options option "title" "Huge Example file" option "operating_currency" "USD" * Commodities 1792-01-01 commodity USD export: "CASH" name: "US Dollar" 1900-01-01 commodity VMMXX export: "MUTF:VMMXX (MONEY:USD)" 1980-05-12 commodity VACHR export: "IGNORE" name: "Employer Vacation Hours" 1980-05-12 commodity IRAUSD export: "IGNORE" name: "US 401k and IRA Contributions" 1995-09-18 commodity VBMPX export: "MUTF:VBMPX" name: "Vanguard Total Bond Market Index Fund Institutional Plus Shares" price: "USD:google/MUTF:VBMPX" 2004-01-20 commodity ITOT export: "NYSEARCA:ITOT" name: "iShares Core S&P Total U.S. Stock Market ETF" price: "USD:google/NYSEARCA:ITOT" 2004-01-26 commodity VHT export: "NYSEARCA:VHT" name: "Vanguard Health Care ETF" price: "USD:google/NYSEARCA:VHT" 2004-11-01 commodity GLD export: "NYSEARCA:GLD" name: "SPDR Gold Trust (ETF)" price: "USD:google/NYSEARCA:GLD" 2007-07-20 commodity VEA export: "NYSEARCA:VEA" name: "Vanguard FTSE Developed Markets ETF" price: "USD:google/NYSEARCA:VEA" 2009-05-01 commodity RGAGX export: "MUTF:RGAGX" name: "American Funds The Growth Fund of America Class R-6" price: "USD:google/MUTF:RGAGX" * Equity Accounts 1980-05-12 open Equity:Opening-Balances 1980-05-12 open Liabilities:AccountsPayable * Banking 2008-01-01 open Assets:US:BofA address: "123 America Street, LargeTown, USA" institution: "Bank of America" phone: "+1.012.345.6789" 2008-01-01 open Assets:US:BofA:Checking USD account: "00234-48574897" 2008-01-01 * "Opening Balance for checking account" Assets:US:BofA:Checking 3057.70 USD Equity:Opening-Balances -3057.70 USD 2008-01-02 balance Assets:US:BofA:Checking 3057.70 USD 2008-01-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2008-01-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2008-01-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2008-01-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -47.85 USD Expenses:Home:Phone 47.85 USD 2008-01-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.95 USD Expenses:Home:Internet 79.95 USD 2008-01-28 balance Assets:US:BofA:Checking 3061.45 USD 2008-02-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2008-02-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2008-02-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2008-02-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -67.47 USD Expenses:Home:Phone 67.47 USD 2008-02-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.14 USD Expenses:Home:Internet 80.14 USD 2008-02-27 balance Assets:US:BofA:Checking 2517.71 USD 2008-03-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2008-03-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2008-03-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2008-03-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -67.89 USD Expenses:Home:Phone 67.89 USD 2008-03-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.90 USD Expenses:Home:Internet 79.90 USD 2008-03-23 balance Assets:US:BofA:Checking 1796.75 USD 2008-04-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2008-04-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2008-04-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2008-04-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -58.34 USD Expenses:Home:Phone 58.34 USD 2008-04-22 balance Assets:US:BofA:Checking 1252.37 USD 2008-04-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.06 USD Expenses:Home:Internet 80.06 USD 2008-05-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2008-05-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2008-05-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2008-05-14 balance Assets:US:BofA:Checking 659.20 USD 2008-05-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -64.47 USD Expenses:Home:Phone 64.47 USD 2008-05-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.94 USD Expenses:Home:Internet 79.94 USD 2008-06-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2008-06-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2008-06-08 balance Assets:US:BofA:Checking 811.99 USD 2008-06-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2008-06-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -60.49 USD Expenses:Home:Phone 60.49 USD 2008-06-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.13 USD Expenses:Home:Internet 80.13 USD 2008-07-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2008-07-04 balance Assets:US:BofA:Checking 1521.07 USD 2008-07-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2008-07-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2008-07-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -63.81 USD Expenses:Home:Phone 63.81 USD 2008-07-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.13 USD Expenses:Home:Internet 80.13 USD 2008-07-24 balance Assets:US:BofA:Checking 3048.85 USD 2008-08-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2008-08-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2008-08-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2008-08-15 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -4000 USD Assets:US:ETrade:Cash 4000 USD 2008-08-18 balance Assets:US:BofA:Checking 1232.13 USD 2008-08-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -78.64 USD Expenses:Home:Phone 78.64 USD 2008-08-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.97 USD Expenses:Home:Internet 79.97 USD 2008-09-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2008-09-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2008-09-09 balance Assets:US:BofA:Checking 1220.12 USD 2008-09-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2008-09-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -41.56 USD Expenses:Home:Phone 41.56 USD 2008-09-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.00 USD Expenses:Home:Internet 80.00 USD 2008-10-04 balance Assets:US:BofA:Checking 5411.46 USD 2008-10-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2008-10-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2008-10-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2008-10-10 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -4500 USD Assets:US:ETrade:Cash 4500 USD 2008-10-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -57.49 USD Expenses:Home:Phone 57.49 USD 2008-10-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.04 USD Expenses:Home:Internet 80.04 USD 2008-10-29 balance Assets:US:BofA:Checking 2821.64 USD 2008-11-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2008-11-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2008-11-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2008-11-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -63.23 USD Expenses:Home:Phone 63.23 USD 2008-11-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.89 USD Expenses:Home:Internet 79.89 USD 2008-11-27 balance Assets:US:BofA:Checking 4465.30 USD 2008-12-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2008-12-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2008-12-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2008-12-20 balance Assets:US:BofA:Checking 6717.92 USD 2008-12-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -44.92 USD Expenses:Home:Phone 44.92 USD 2008-12-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.25 USD Expenses:Home:Internet 80.25 USD 2009-01-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2009-01-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2009-01-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2009-01-19 balance Assets:US:BofA:Checking 6358.84 USD 2009-01-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -60.30 USD Expenses:Home:Phone 60.30 USD 2009-01-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.13 USD Expenses:Home:Internet 80.13 USD 2009-02-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2009-02-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2009-02-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2009-02-16 balance Assets:US:BofA:Checking 5827.90 USD 2009-02-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -56.04 USD Expenses:Home:Phone 56.04 USD 2009-02-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.08 USD Expenses:Home:Internet 80.08 USD 2009-03-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2009-03-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2009-03-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2009-03-10 balance Assets:US:BofA:Checking 4573.38 USD 2009-03-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -57.49 USD Expenses:Home:Phone 57.49 USD 2009-03-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.02 USD Expenses:Home:Internet 80.02 USD 2009-03-30 balance Assets:US:BofA:Checking 5675.94 USD 2009-04-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2009-04-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2009-04-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2009-04-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -53.03 USD Expenses:Home:Phone 53.03 USD 2009-04-22 balance Assets:US:BofA:Checking 3874.10 USD 2009-04-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.97 USD Expenses:Home:Internet 79.97 USD 2009-05-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2009-05-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2009-05-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2009-05-19 balance Assets:US:BofA:Checking 3311.13 USD 2009-05-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -51.53 USD Expenses:Home:Phone 51.53 USD 2009-05-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.07 USD Expenses:Home:Internet 80.07 USD 2009-06-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2009-06-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2009-06-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2009-06-17 balance Assets:US:BofA:Checking 2650.53 USD 2009-06-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -64.23 USD Expenses:Home:Phone 64.23 USD 2009-06-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.05 USD Expenses:Home:Internet 80.05 USD 2009-07-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2009-07-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2009-07-08 balance Assets:US:BofA:Checking 3103.45 USD 2009-07-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2009-07-17 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -3500 USD Assets:US:ETrade:Cash 3500 USD 2009-07-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -52.26 USD Expenses:Home:Phone 52.26 USD 2009-07-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.12 USD Expenses:Home:Internet 80.12 USD 2009-08-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2009-08-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2009-08-05 balance Assets:US:BofA:Checking 1467.58 USD 2009-08-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2009-08-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -45.86 USD Expenses:Home:Phone 45.86 USD 2009-08-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.06 USD Expenses:Home:Internet 80.06 USD 2009-08-30 balance Assets:US:BofA:Checking 5615.43 USD 2009-09-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2009-09-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2009-09-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2009-09-11 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -5000 USD Assets:US:ETrade:Cash 5000 USD 2009-09-19 balance Assets:US:BofA:Checking 634.45 USD 2009-09-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -52.64 USD Expenses:Home:Phone 52.64 USD 2009-09-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.22 USD Expenses:Home:Internet 80.22 USD 2009-10-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2009-10-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2009-10-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2009-10-14 balance Assets:US:BofA:Checking 2351.29 USD 2009-10-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -49.81 USD Expenses:Home:Phone 49.81 USD 2009-10-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.86 USD Expenses:Home:Internet 79.86 USD 2009-10-23 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -3500 USD Assets:US:ETrade:Cash 3500 USD 2009-11-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2009-11-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2009-11-07 balance Assets:US:BofA:Checking 1418.82 USD 2009-11-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2009-11-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -53.39 USD Expenses:Home:Phone 53.39 USD 2009-11-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.00 USD Expenses:Home:Internet 80.00 USD 2009-11-29 balance Assets:US:BofA:Checking 3115.03 USD 2009-12-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2009-12-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2009-12-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2009-12-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -55.37 USD Expenses:Home:Phone 55.37 USD 2009-12-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.01 USD Expenses:Home:Internet 80.01 USD 2009-12-27 balance Assets:US:BofA:Checking 5035.61 USD 2010-01-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2010-01-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2010-01-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2010-01-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -72.99 USD Expenses:Home:Phone 72.99 USD 2010-01-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.94 USD Expenses:Home:Internet 79.94 USD 2010-01-22 balance Assets:US:BofA:Checking 5914.66 USD 2010-02-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2010-02-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2010-02-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2010-02-12 balance Assets:US:BofA:Checking 5578.22 USD 2010-02-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -55.77 USD Expenses:Home:Phone 55.77 USD 2010-02-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.06 USD Expenses:Home:Internet 80.06 USD 2010-03-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2010-03-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2010-03-07 balance Assets:US:BofA:Checking 4388.99 USD 2010-03-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2010-03-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -60.67 USD Expenses:Home:Phone 60.67 USD 2010-03-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.98 USD Expenses:Home:Internet 79.98 USD 2010-03-27 balance Assets:US:BofA:Checking 5881.16 USD 2010-04-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2010-04-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2010-04-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2010-04-19 balance Assets:US:BofA:Checking 4229.26 USD 2010-04-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -48.38 USD Expenses:Home:Phone 48.38 USD 2010-04-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.89 USD Expenses:Home:Internet 79.89 USD 2010-05-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2010-05-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2010-05-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2010-05-15 balance Assets:US:BofA:Checking 3708.28 USD 2010-05-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -63.63 USD Expenses:Home:Phone 63.63 USD 2010-05-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.93 USD Expenses:Home:Internet 79.93 USD 2010-06-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2010-06-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2010-06-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2010-06-11 balance Assets:US:BofA:Checking 3452.66 USD 2010-06-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -60.31 USD Expenses:Home:Phone 60.31 USD 2010-06-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.92 USD Expenses:Home:Internet 79.92 USD 2010-07-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2010-07-05 balance Assets:US:BofA:Checking 6009.63 USD 2010-07-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2010-07-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2010-07-16 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -3000 USD Assets:US:ETrade:Cash 3000 USD 2010-07-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -45.49 USD Expenses:Home:Phone 45.49 USD 2010-07-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.00 USD Expenses:Home:Internet 80.00 USD 2010-08-02 balance Assets:US:BofA:Checking 3891.49 USD 2010-08-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2010-08-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2010-08-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2010-08-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -62.44 USD Expenses:Home:Phone 62.44 USD 2010-08-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.96 USD Expenses:Home:Internet 79.96 USD 2010-08-27 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -3000 USD Assets:US:ETrade:Cash 3000 USD 2010-08-29 balance Assets:US:BofA:Checking 2651.89 USD 2010-09-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2010-09-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2010-09-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2010-09-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -28.91 USD Expenses:Home:Phone 28.91 USD 2010-09-19 balance Assets:US:BofA:Checking 2067.96 USD 2010-09-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.90 USD Expenses:Home:Internet 79.90 USD 2010-10-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2010-10-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2010-10-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2010-10-08 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -3500 USD Assets:US:ETrade:Cash 3500 USD 2010-10-11 balance Assets:US:BofA:Checking 443.69 USD 2010-10-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -72.14 USD Expenses:Home:Phone 72.14 USD 2010-10-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.93 USD Expenses:Home:Internet 79.93 USD 2010-11-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2010-11-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2010-11-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2010-11-10 balance Assets:US:BofA:Checking 2923.82 USD 2010-11-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -60.09 USD Expenses:Home:Phone 60.09 USD 2010-11-19 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -3000 USD Assets:US:ETrade:Cash 3000 USD 2010-11-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.92 USD Expenses:Home:Internet 79.92 USD 2010-12-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2010-12-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2010-12-07 balance Assets:US:BofA:Checking 1766.88 USD 2010-12-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2010-12-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -61.50 USD Expenses:Home:Phone 61.50 USD 2010-12-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.11 USD Expenses:Home:Internet 80.11 USD 2010-12-27 balance Assets:US:BofA:Checking 3492.96 USD 2011-01-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2011-01-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2011-01-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2011-01-18 balance Assets:US:BofA:Checking 4687.68 USD 2011-01-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -55.05 USD Expenses:Home:Phone 55.05 USD 2011-01-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.05 USD Expenses:Home:Internet 80.05 USD 2011-02-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2011-02-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2011-02-08 balance Assets:US:BofA:Checking 2871.81 USD 2011-02-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2011-02-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -67.36 USD Expenses:Home:Phone 67.36 USD 2011-02-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.09 USD Expenses:Home:Internet 80.09 USD 2011-03-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2011-03-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2011-03-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2011-03-10 balance Assets:US:BofA:Checking 2891.56 USD 2011-03-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -78.40 USD Expenses:Home:Phone 78.40 USD 2011-03-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.10 USD Expenses:Home:Internet 80.10 USD 2011-04-03 balance Assets:US:BofA:Checking 3865.71 USD 2011-04-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2011-04-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2011-04-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2011-04-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -57.52 USD Expenses:Home:Phone 57.52 USD 2011-04-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.05 USD Expenses:Home:Internet 80.05 USD 2011-05-03 balance Assets:US:BofA:Checking 3484.77 USD 2011-05-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2011-05-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2011-05-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2011-05-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -47.20 USD Expenses:Home:Phone 47.20 USD 2011-05-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.11 USD Expenses:Home:Internet 80.11 USD 2011-06-01 balance Assets:US:BofA:Checking 3185.98 USD 2011-06-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2011-06-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2011-06-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2011-06-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -62.44 USD Expenses:Home:Phone 62.44 USD 2011-06-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.14 USD Expenses:Home:Internet 80.14 USD 2011-06-29 balance Assets:US:BofA:Checking 2480.53 USD 2011-07-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2011-07-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2011-07-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2011-07-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -40.48 USD Expenses:Home:Phone 40.48 USD 2011-07-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.98 USD Expenses:Home:Internet 79.98 USD 2011-07-28 balance Assets:US:BofA:Checking 2106.77 USD 2011-08-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2011-08-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2011-08-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2011-08-12 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -3000 USD Assets:US:ETrade:Cash 3000 USD 2011-08-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -62.12 USD Expenses:Home:Phone 62.12 USD 2011-08-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.95 USD Expenses:Home:Internet 79.95 USD 2011-08-25 balance Assets:US:BofA:Checking 1139.59 USD 2011-09-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2011-09-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2011-09-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2011-09-19 balance Assets:US:BofA:Checking 2988.57 USD 2011-09-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -45.61 USD Expenses:Home:Phone 45.61 USD 2011-09-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.04 USD Expenses:Home:Internet 80.04 USD 2011-10-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2011-10-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2011-10-07 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -4000 USD Assets:US:ETrade:Cash 4000 USD 2011-10-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2011-10-17 balance Assets:US:BofA:Checking 627.38 USD 2011-10-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -48.99 USD Expenses:Home:Phone 48.99 USD 2011-10-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.10 USD Expenses:Home:Internet 80.10 USD 2011-11-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2011-11-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2011-11-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2011-11-11 balance Assets:US:BofA:Checking 3130.49 USD 2011-11-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -53.02 USD Expenses:Home:Phone 53.02 USD 2011-11-18 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -4000 USD Assets:US:ETrade:Cash 4000 USD 2011-11-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.88 USD Expenses:Home:Internet 79.88 USD 2011-12-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2011-12-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2011-12-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2011-12-10 balance Assets:US:BofA:Checking 439.31 USD 2011-12-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -56.25 USD Expenses:Home:Phone 56.25 USD 2011-12-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.98 USD Expenses:Home:Internet 79.98 USD 2012-01-02 balance Assets:US:BofA:Checking 5724.28 USD 2012-01-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2012-01-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2012-01-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2012-01-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -70.47 USD Expenses:Home:Phone 70.47 USD 2012-01-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.03 USD Expenses:Home:Internet 80.03 USD 2012-01-29 balance Assets:US:BofA:Checking 5098.11 USD 2012-02-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2012-02-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2012-02-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2012-02-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -54.59 USD Expenses:Home:Phone 54.59 USD 2012-02-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.14 USD Expenses:Home:Internet 80.14 USD 2012-02-26 balance Assets:US:BofA:Checking 4617.63 USD 2012-03-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2012-03-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2012-03-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2012-03-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -64.39 USD Expenses:Home:Phone 64.39 USD 2012-03-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.96 USD Expenses:Home:Internet 79.96 USD 2012-03-27 balance Assets:US:BofA:Checking 3885.14 USD 2012-04-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2012-04-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2012-04-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2012-04-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -63.28 USD Expenses:Home:Phone 63.28 USD 2012-04-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.18 USD Expenses:Home:Internet 80.18 USD 2012-04-23 balance Assets:US:BofA:Checking 3319.01 USD 2012-05-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2012-05-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2012-05-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2012-05-15 balance Assets:US:BofA:Checking 1906.18 USD 2012-05-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -48.74 USD Expenses:Home:Phone 48.74 USD 2012-05-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.98 USD Expenses:Home:Internet 79.98 USD 2012-06-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2012-06-05 balance Assets:US:BofA:Checking 4474.66 USD 2012-06-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2012-06-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2012-06-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -72.60 USD Expenses:Home:Phone 72.60 USD 2012-06-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.93 USD Expenses:Home:Internet 79.93 USD 2012-06-27 balance Assets:US:BofA:Checking 2567.16 USD 2012-07-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2012-07-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2012-07-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2012-07-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -63.79 USD Expenses:Home:Phone 63.79 USD 2012-07-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.10 USD Expenses:Home:Internet 80.10 USD 2012-07-23 balance Assets:US:BofA:Checking 2007.47 USD 2012-08-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2012-08-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2012-08-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2012-08-10 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -3500 USD Assets:US:ETrade:Cash 3500 USD 2012-08-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -69.38 USD Expenses:Home:Phone 69.38 USD 2012-08-20 balance Assets:US:BofA:Checking 312.46 USD 2012-08-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.22 USD Expenses:Home:Internet 80.22 USD 2012-09-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2012-09-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2012-09-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2012-09-12 balance Assets:US:BofA:Checking 2219.92 USD 2012-09-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -62.54 USD Expenses:Home:Phone 62.54 USD 2012-09-21 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -3500 USD Assets:US:ETrade:Cash 3500 USD 2012-09-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.84 USD Expenses:Home:Internet 79.84 USD 2012-10-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2012-10-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2012-10-09 balance Assets:US:BofA:Checking 1274.74 USD 2012-10-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2012-10-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -57.87 USD Expenses:Home:Phone 57.87 USD 2012-10-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.08 USD Expenses:Home:Internet 80.08 USD 2012-11-01 balance Assets:US:BofA:Checking 2797.55 USD 2012-11-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2012-11-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2012-11-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2012-11-16 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -3500 USD Assets:US:ETrade:Cash 3500 USD 2012-11-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -59.10 USD Expenses:Home:Phone 59.10 USD 2012-11-21 balance Assets:US:BofA:Checking 1409.76 USD 2012-11-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.14 USD Expenses:Home:Internet 80.14 USD 2012-12-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2012-12-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2012-12-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2012-12-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -44.69 USD Expenses:Home:Phone 44.69 USD 2012-12-20 balance Assets:US:BofA:Checking 3331.67 USD 2012-12-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.99 USD Expenses:Home:Internet 79.99 USD 2013-01-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2013-01-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2013-01-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2013-01-13 balance Assets:US:BofA:Checking 4239.47 USD 2013-01-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -80.74 USD Expenses:Home:Phone 80.74 USD 2013-01-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.10 USD Expenses:Home:Internet 80.10 USD 2013-02-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2013-02-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2013-02-07 balance Assets:US:BofA:Checking 3025.23 USD 2013-02-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2013-02-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -55.47 USD Expenses:Home:Phone 55.47 USD 2013-02-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.03 USD Expenses:Home:Internet 80.03 USD 2013-02-27 balance Assets:US:BofA:Checking 4937.58 USD 2013-03-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2013-03-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2013-03-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2013-03-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -56.57 USD Expenses:Home:Phone 56.57 USD 2013-03-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.96 USD Expenses:Home:Internet 79.96 USD 2013-03-29 balance Assets:US:BofA:Checking 3734.07 USD 2013-04-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2013-04-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2013-04-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2013-04-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -60.17 USD Expenses:Home:Phone 60.17 USD 2013-04-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.02 USD Expenses:Home:Internet 80.02 USD 2013-04-27 balance Assets:US:BofA:Checking 3083.40 USD 2013-05-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2013-05-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2013-05-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2013-05-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -52.76 USD Expenses:Home:Phone 52.76 USD 2013-05-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.99 USD Expenses:Home:Internet 79.99 USD 2013-05-26 balance Assets:US:BofA:Checking 2446.83 USD 2013-06-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2013-06-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2013-06-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2013-06-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -59.90 USD Expenses:Home:Phone 59.90 USD 2013-06-19 balance Assets:US:BofA:Checking 2270.81 USD 2013-06-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.12 USD Expenses:Home:Internet 80.12 USD 2013-07-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2013-07-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2013-07-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2013-07-19 balance Assets:US:BofA:Checking 1796.49 USD 2013-07-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -54.08 USD Expenses:Home:Phone 54.08 USD 2013-07-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.11 USD Expenses:Home:Internet 80.11 USD 2013-08-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2013-08-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2013-08-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2013-08-12 balance Assets:US:BofA:Checking 2908.22 USD 2013-08-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -53.17 USD Expenses:Home:Phone 53.17 USD 2013-08-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.89 USD Expenses:Home:Internet 79.89 USD 2013-09-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2013-09-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2013-09-05 balance Assets:US:BofA:Checking 2921.76 USD 2013-09-06 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -4000 USD Assets:US:ETrade:Cash 4000 USD 2013-09-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2013-09-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -60.77 USD Expenses:Home:Phone 60.77 USD 2013-09-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.02 USD Expenses:Home:Internet 80.02 USD 2013-09-27 balance Assets:US:BofA:Checking 2941.15 USD 2013-10-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2013-10-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2013-10-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2013-10-17 balance Assets:US:BofA:Checking 2494.56 USD 2013-10-18 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -4000 USD Assets:US:ETrade:Cash 4000 USD 2013-10-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -69.11 USD Expenses:Home:Phone 69.11 USD 2013-10-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.94 USD Expenses:Home:Internet 79.94 USD 2013-11-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2013-11-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2013-11-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2013-11-14 balance Assets:US:BofA:Checking 394.38 USD 2013-11-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -63.02 USD Expenses:Home:Phone 63.02 USD 2013-11-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.81 USD Expenses:Home:Internet 79.81 USD 2013-12-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2013-12-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2013-12-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2013-12-11 balance Assets:US:BofA:Checking 2305.77 USD 2013-12-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -41.00 USD Expenses:Home:Phone 41.00 USD 2013-12-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.00 USD Expenses:Home:Internet 80.00 USD 2014-01-02 balance Assets:US:BofA:Checking 7605.97 USD 2014-01-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2014-01-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2014-01-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2014-01-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -62.23 USD Expenses:Home:Phone 62.23 USD 2014-01-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.03 USD Expenses:Home:Internet 80.03 USD 2014-01-25 balance Assets:US:BofA:Checking 7414.78 USD 2014-02-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2014-02-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2014-02-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2014-02-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -39.86 USD Expenses:Home:Phone 39.86 USD 2014-02-23 balance Assets:US:BofA:Checking 7021.60 USD 2014-02-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.96 USD Expenses:Home:Internet 79.96 USD 2014-03-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2014-03-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2014-03-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2014-03-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -50.52 USD Expenses:Home:Phone 50.52 USD 2014-03-21 balance Assets:US:BofA:Checking 6547.45 USD 2014-03-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.76 USD Expenses:Home:Internet 79.76 USD 2014-04-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2014-04-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2014-04-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2014-04-15 balance Assets:US:BofA:Checking 3940.02 USD 2014-04-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -63.55 USD Expenses:Home:Phone 63.55 USD 2014-04-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.19 USD Expenses:Home:Internet 80.19 USD 2014-05-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2014-05-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2014-05-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2014-05-13 balance Assets:US:BofA:Checking 3276.71 USD 2014-05-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -57.98 USD Expenses:Home:Phone 57.98 USD 2014-05-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.83 USD Expenses:Home:Internet 79.83 USD 2014-06-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2014-06-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2014-06-08 balance Assets:US:BofA:Checking 3436.10 USD 2014-06-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2014-06-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -55.34 USD Expenses:Home:Phone 55.34 USD 2014-06-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.94 USD Expenses:Home:Internet 79.94 USD 2014-07-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2014-07-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2014-07-07 balance Assets:US:BofA:Checking 3072.49 USD 2014-07-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2014-07-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -56.79 USD Expenses:Home:Phone 56.79 USD 2014-07-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.12 USD Expenses:Home:Internet 80.12 USD 2014-07-28 balance Assets:US:BofA:Checking 5586.52 USD 2014-08-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2014-08-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2014-08-08 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -4500 USD Assets:US:ETrade:Cash 4500 USD 2014-08-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2014-08-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -69.75 USD Expenses:Home:Phone 69.75 USD 2014-08-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.15 USD Expenses:Home:Internet 80.15 USD 2014-08-23 balance Assets:US:BofA:Checking 2861.95 USD 2014-09-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2014-09-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2014-09-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2014-09-12 balance Assets:US:BofA:Checking 2299.98 USD 2014-09-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -52.58 USD Expenses:Home:Phone 52.58 USD 2014-09-19 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -3500 USD Assets:US:ETrade:Cash 3500 USD 2014-09-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.96 USD Expenses:Home:Internet 79.96 USD 2014-10-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2014-10-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2014-10-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2014-10-09 balance Assets:US:BofA:Checking 1299.64 USD 2014-10-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -64.12 USD Expenses:Home:Phone 64.12 USD 2014-10-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.00 USD Expenses:Home:Internet 80.00 USD 2014-11-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2014-11-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2014-11-07 balance Assets:US:BofA:Checking 3162.33 USD 2014-11-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2014-11-14 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -4000 USD Assets:US:ETrade:Cash 4000 USD 2014-11-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -69.97 USD Expenses:Home:Phone 69.97 USD 2014-11-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.01 USD Expenses:Home:Internet 80.01 USD 2014-11-29 balance Assets:US:BofA:Checking 3509.93 USD 2014-12-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2014-12-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2014-12-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2014-12-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -52.31 USD Expenses:Home:Phone 52.31 USD 2014-12-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.31 USD Expenses:Home:Internet 80.31 USD 2014-12-23 balance Assets:US:BofA:Checking 3172.41 USD 2015-01-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-01-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-01-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-01-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -60.52 USD Expenses:Home:Phone 60.52 USD 2015-01-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.96 USD Expenses:Home:Internet 79.96 USD 2015-01-22 balance Assets:US:BofA:Checking 4153.05 USD 2015-02-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-02-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-02-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-02-14 balance Assets:US:BofA:Checking 3809.16 USD 2015-02-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -56.64 USD Expenses:Home:Phone 56.64 USD 2015-02-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.09 USD Expenses:Home:Internet 80.09 USD 2015-03-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-03-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-03-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-03-12 balance Assets:US:BofA:Checking 3270.40 USD 2015-03-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -41.73 USD Expenses:Home:Phone 41.73 USD 2015-03-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.91 USD Expenses:Home:Internet 79.91 USD 2015-04-04 balance Assets:US:BofA:Checking 4948.24 USD 2015-04-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-04-06 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-04-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-04-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -53.04 USD Expenses:Home:Phone 53.04 USD 2015-04-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.03 USD Expenses:Home:Internet 80.03 USD 2015-04-28 balance Assets:US:BofA:Checking 3037.03 USD 2015-05-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-05-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-05-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-05-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -58.40 USD Expenses:Home:Phone 58.40 USD 2015-05-21 balance Assets:US:BofA:Checking 3077.27 USD 2015-05-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.00 USD Expenses:Home:Internet 80.00 USD 2015-06-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-06-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-06-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-06-11 balance Assets:US:BofA:Checking 961.71 USD 2015-06-18 * "Verizon Wireless" "" Assets:US:BofA:Checking -62.31 USD Expenses:Home:Phone 62.31 USD 2015-06-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.09 USD Expenses:Home:Internet 80.09 USD 2015-07-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-07-04 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-07-08 balance Assets:US:BofA:Checking 1116.51 USD 2015-07-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-07-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -59.57 USD Expenses:Home:Phone 59.57 USD 2015-07-21 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -79.93 USD Expenses:Home:Internet 79.93 USD 2015-08-01 balance Assets:US:BofA:Checking 3002.60 USD 2015-08-03 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-08-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-08-09 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-08-19 * "Verizon Wireless" "" Assets:US:BofA:Checking -61.96 USD Expenses:Home:Phone 61.96 USD 2015-08-21 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -4000 USD Assets:US:ETrade:Cash 4000 USD 2015-08-23 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.03 USD Expenses:Home:Internet 80.03 USD 2015-08-30 balance Assets:US:BofA:Checking 882.27 USD 2015-09-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD 2015-09-05 * "RiverBank Properties" "Paying the rent" Assets:US:BofA:Checking -2400.00 USD Expenses:Home:Rent 2400.00 USD 2015-09-08 * "EDISON POWER" "" Assets:US:BofA:Checking -65.00 USD Expenses:Home:Electricity 65.00 USD 2015-09-20 * "Verizon Wireless" "" Assets:US:BofA:Checking -75.51 USD Expenses:Home:Phone 75.51 USD 2015-09-22 * "Wine-Tarner Cable" "" Assets:US:BofA:Checking -80.12 USD Expenses:Home:Internet 80.12 USD 2015-09-28 balance Assets:US:BofA:Checking 2889.36 USD 2015-10-02 * "Transfering accumulated savings to other account" Assets:US:BofA:Checking -5000 USD Assets:US:ETrade:Cash 5000 USD 2015-10-04 * "BANK FEES" "Monthly bank fee" Assets:US:BofA:Checking -4.00 USD Expenses:Financial:Fees 4.00 USD * Credit-Cards 1980-05-12 open Liabilities:US:Chase:Slate USD 2008-01-06 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -38.86 USD Expenses:Food:Restaurant 38.86 USD 2008-01-07 * "Chichipotle" "Eating out with work buddies" Liabilities:US:Chase:Slate -34.66 USD Expenses:Food:Restaurant 34.66 USD 2008-01-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 100.65 USD Assets:US:BofA:Checking -100.65 USD 2008-01-12 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -27.13 USD Expenses:Food:Restaurant 27.13 USD 2008-01-16 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -27.11 USD Expenses:Food:Restaurant 27.11 USD 2008-01-18 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -62.77 USD Expenses:Food:Groceries 62.77 USD 2008-01-19 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -33.87 USD Expenses:Food:Restaurant 33.87 USD 2008-01-20 * "Kin Soy" "Eating out with Bill" Liabilities:US:Chase:Slate -12.58 USD Expenses:Food:Restaurant 12.58 USD 2008-01-24 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -13.77 USD Expenses:Food:Restaurant 13.77 USD 2008-01-26 balance Liabilities:US:Chase:Slate -150.10 USD 2008-01-27 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -48.05 USD Expenses:Food:Restaurant 48.05 USD 2008-01-28 * "Goba Goba" "Eating out alone" Liabilities:US:Chase:Slate -51.46 USD Expenses:Food:Restaurant 51.46 USD 2008-01-31 * "China Garden" "Eating out with Julie" Liabilities:US:Chase:Slate -39.11 USD Expenses:Food:Restaurant 39.11 USD 2008-02-01 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2008-02-02 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -62.50 USD Expenses:Food:Groceries 62.50 USD 2008-02-03 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -40.17 USD Expenses:Food:Restaurant 40.17 USD 2008-02-06 * "Jewel of Morroco" "Eating out with Julie" Liabilities:US:Chase:Slate -81.82 USD Expenses:Food:Restaurant 81.82 USD 2008-02-07 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -35.12 USD Expenses:Food:Restaurant 35.12 USD 2008-02-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 628.33 USD Assets:US:BofA:Checking -628.33 USD 2008-02-08 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -34.41 USD Expenses:Food:Restaurant 34.41 USD 2008-02-12 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -31.97 USD Expenses:Food:Restaurant 31.97 USD 2008-02-13 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -31.18 USD Expenses:Food:Restaurant 31.18 USD 2008-02-14 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -72.06 USD Expenses:Food:Groceries 72.06 USD 2008-02-17 balance Liabilities:US:Chase:Slate -169.62 USD 2008-02-17 * "Cafe Modagor" "Eating out with Bill" Liabilities:US:Chase:Slate -34.78 USD Expenses:Food:Restaurant 34.78 USD 2008-02-20 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -36.40 USD Expenses:Food:Restaurant 36.40 USD 2008-02-22 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -33.17 USD Expenses:Food:Restaurant 33.17 USD 2008-02-23 * "China Garden" "Eating out " Liabilities:US:Chase:Slate -30.42 USD Expenses:Food:Restaurant 30.42 USD 2008-02-25 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -35.97 USD Expenses:Food:Restaurant 35.97 USD 2008-02-26 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -33.74 USD Expenses:Food:Restaurant 33.74 USD 2008-02-27 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -45.38 USD Expenses:Food:Restaurant 45.38 USD 2008-02-28 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -24.60 USD Expenses:Food:Restaurant 24.60 USD 2008-02-29 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -68.74 USD Expenses:Food:Restaurant 68.74 USD 2008-03-04 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -38.43 USD Expenses:Food:Restaurant 38.43 USD 2008-03-04 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2008-03-05 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -57.71 USD Expenses:Food:Groceries 57.71 USD 2008-03-06 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -41.30 USD Expenses:Food:Restaurant 41.30 USD 2008-03-07 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -35.11 USD Expenses:Food:Restaurant 35.11 USD 2008-03-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 805.37 USD Assets:US:BofA:Checking -805.37 USD 2008-03-09 * "Uncle Boons" "Eating out with Julie" Liabilities:US:Chase:Slate -27.55 USD Expenses:Food:Restaurant 27.55 USD 2008-03-14 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -22.05 USD Expenses:Food:Restaurant 22.05 USD 2008-03-15 * "Cafe Modagor" "Eating out with Bill" Liabilities:US:Chase:Slate -36.77 USD Expenses:Food:Restaurant 36.77 USD 2008-03-15 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -73.13 USD Expenses:Food:Groceries 73.13 USD 2008-03-16 balance Liabilities:US:Chase:Slate -159.50 USD 2008-03-18 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -23.84 USD Expenses:Food:Restaurant 23.84 USD 2008-03-21 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -23.04 USD Expenses:Food:Restaurant 23.04 USD 2008-03-22 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -82.61 USD Expenses:Food:Restaurant 82.61 USD 2008-03-22 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -80.46 USD Expenses:Food:Groceries 80.46 USD 2008-03-25 * "Chichipotle" "Eating out with Natasha" Liabilities:US:Chase:Slate -39.25 USD Expenses:Food:Restaurant 39.25 USD 2008-03-30 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -48.92 USD Expenses:Food:Restaurant 48.92 USD 2008-04-01 * "China Garden" "Eating out with Bill" Liabilities:US:Chase:Slate -17.39 USD Expenses:Food:Restaurant 17.39 USD 2008-04-06 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -10.62 USD Expenses:Food:Restaurant 10.62 USD 2008-04-06 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -84.07 USD Expenses:Food:Groceries 84.07 USD 2008-04-06 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2008-04-07 balance Liabilities:US:Chase:Slate -689.70 USD 2008-04-11 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -28.54 USD Expenses:Food:Restaurant 28.54 USD 2008-04-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 718.24 USD Assets:US:BofA:Checking -718.24 USD 2008-04-15 * "Cafe Modagor" "Eating out with Bill" Liabilities:US:Chase:Slate -30.68 USD Expenses:Food:Restaurant 30.68 USD 2008-04-17 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -99.32 USD Expenses:Food:Groceries 99.32 USD 2008-04-18 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -28.82 USD Expenses:Food:Restaurant 28.82 USD 2008-04-19 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -45.08 USD Expenses:Food:Restaurant 45.08 USD 2008-04-21 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -30.31 USD Expenses:Food:Restaurant 30.31 USD 2008-04-22 * "Uncle Boons" "Eating out " Liabilities:US:Chase:Slate -24.74 USD Expenses:Food:Restaurant 24.74 USD 2008-04-22 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -65.81 USD Expenses:Food:Groceries 65.81 USD 2008-04-24 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -46.27 USD Expenses:Food:Restaurant 46.27 USD 2008-04-27 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -31.09 USD Expenses:Food:Restaurant 31.09 USD 2008-04-27 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -97.88 USD Expenses:Food:Groceries 97.88 USD 2008-04-30 balance Liabilities:US:Chase:Slate -500.00 USD 2008-04-30 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -59.90 USD Expenses:Food:Restaurant 59.90 USD 2008-05-04 * "Kin Soy" "Eating out with Julie" Liabilities:US:Chase:Slate -48.45 USD Expenses:Food:Restaurant 48.45 USD 2008-05-07 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2008-05-08 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 745.31 USD Assets:US:BofA:Checking -745.31 USD 2008-05-09 * "China Garden" "Eating out with Julie" Liabilities:US:Chase:Slate -16.96 USD Expenses:Food:Restaurant 16.96 USD 2008-05-13 * "Goba Goba" "Eating out alone" Liabilities:US:Chase:Slate -33.63 USD Expenses:Food:Restaurant 33.63 USD 2008-05-16 * "Kin Soy" "Eating out after work" Liabilities:US:Chase:Slate -38.99 USD Expenses:Food:Restaurant 38.99 USD 2008-05-16 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -87.59 USD Expenses:Food:Groceries 87.59 USD 2008-05-18 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -32.73 USD Expenses:Food:Restaurant 32.73 USD 2008-05-22 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -62.75 USD Expenses:Food:Restaurant 62.75 USD 2008-05-23 balance Liabilities:US:Chase:Slate -255.69 USD 2008-05-25 * "Cafe Modagor" "Eating out with Bill" Liabilities:US:Chase:Slate -30.07 USD Expenses:Food:Restaurant 30.07 USD 2008-05-27 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -24.23 USD Expenses:Food:Restaurant 24.23 USD 2008-05-28 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -26.84 USD Expenses:Food:Restaurant 26.84 USD 2008-05-31 * "Kin Soy" "Eating out after work" Liabilities:US:Chase:Slate -27.12 USD Expenses:Food:Restaurant 27.12 USD 2008-06-02 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -39.20 USD Expenses:Food:Restaurant 39.20 USD 2008-06-02 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -63.34 USD Expenses:Food:Groceries 63.34 USD 2008-06-03 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2008-06-04 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -36.83 USD Expenses:Food:Restaurant 36.83 USD 2008-06-09 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -21.68 USD Expenses:Food:Restaurant 21.68 USD 2008-06-11 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -41.50 USD Expenses:Food:Restaurant 41.50 USD 2008-06-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 686.50 USD Assets:US:BofA:Checking -686.50 USD 2008-06-14 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -145.89 USD Expenses:Food:Groceries 145.89 USD 2008-06-15 balance Liabilities:US:Chase:Slate -145.89 USD 2008-06-15 * "Rose Flower" "Eating out with work buddies" Liabilities:US:Chase:Slate -19.38 USD Expenses:Food:Restaurant 19.38 USD 2008-06-17 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -47.26 USD Expenses:Food:Restaurant 47.26 USD 2008-06-20 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -29.10 USD Expenses:Food:Restaurant 29.10 USD 2008-06-23 * "China Garden" "Eating out with Bill" Liabilities:US:Chase:Slate -22.91 USD Expenses:Food:Restaurant 22.91 USD 2008-06-24 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -37.31 USD Expenses:Food:Groceries 37.31 USD 2008-06-27 * "Jewel of Morroco" "Eating out with Natasha" Liabilities:US:Chase:Slate -40.38 USD Expenses:Food:Restaurant 40.38 USD 2008-06-30 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -21.45 USD Expenses:Food:Restaurant 21.45 USD 2008-06-30 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2008-07-02 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -24.50 USD Expenses:Food:Restaurant 24.50 USD 2008-07-03 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -96.81 USD Expenses:Food:Restaurant 96.81 USD 2008-07-06 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -33.69 USD Expenses:Food:Restaurant 33.69 USD 2008-07-07 balance Liabilities:US:Chase:Slate -638.68 USD 2008-07-07 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -110.12 USD Expenses:Food:Groceries 110.12 USD 2008-07-08 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 809.88 USD Assets:US:BofA:Checking -809.88 USD 2008-07-09 * "Goba Goba" "Eating out alone" Liabilities:US:Chase:Slate -61.08 USD Expenses:Food:Restaurant 61.08 USD 2008-07-10 event "location" "San Francisco" 2008-07-11 * "Pizza Delfina" "" #trip-san-francisco-2008 Liabilities:US:Chase:Slate -20.55 USD Expenses:Food:Restaurant 20.55 USD 2008-07-12 * "Pizza Delfina" "" #trip-san-francisco-2008 Liabilities:US:Chase:Slate -20.96 USD Expenses:Food:Restaurant 20.96 USD 2008-07-12 * "Waterbar" "" #trip-san-francisco-2008 Liabilities:US:Chase:Slate -54.85 USD Expenses:Food:Restaurant 54.85 USD 2008-07-13 * "Waterbar" "" #trip-san-francisco-2008 Liabilities:US:Chase:Slate -46.29 USD Expenses:Food:Restaurant 46.29 USD 2008-07-14 * "Bar Crudo" "" #trip-san-francisco-2008 Liabilities:US:Chase:Slate -74.60 USD Expenses:Food:Restaurant 74.60 USD 2008-07-14 * "Pizza Delfina" "" #trip-san-francisco-2008 Liabilities:US:Chase:Slate -18.52 USD Expenses:Food:Restaurant 18.52 USD 2008-07-15 * "Bar Crudo" "" #trip-san-francisco-2008 Liabilities:US:Chase:Slate -71.97 USD Expenses:Food:Restaurant 71.97 USD 2008-07-15 * "Starbucks" "" #trip-san-francisco-2008 Liabilities:US:Chase:Slate -5.94 USD Expenses:Food:Coffee 5.94 USD 2008-07-16 * "Pizza Delfina" "" #trip-san-francisco-2008 Liabilities:US:Chase:Slate -18.08 USD Expenses:Food:Restaurant 18.08 USD 2008-07-16 * "Waterbar" "" #trip-san-francisco-2008 Liabilities:US:Chase:Slate -48.77 USD Expenses:Food:Restaurant 48.77 USD 2008-07-16 * "Starbucks" "" #trip-san-francisco-2008 Liabilities:US:Chase:Slate -5.44 USD Expenses:Food:Coffee 5.44 USD 2008-07-17 * "Waterbar" "" #trip-san-francisco-2008 Liabilities:US:Chase:Slate -54.73 USD Expenses:Food:Restaurant 54.73 USD 2008-07-17 * "Mission Chinese Food" "" #trip-san-francisco-2008 Liabilities:US:Chase:Slate -22.53 USD Expenses:Food:Restaurant 22.53 USD 2008-07-18 * "Bar Crudo" "" #trip-san-francisco-2008 Liabilities:US:Chase:Slate -70.93 USD Expenses:Food:Restaurant 70.93 USD 2008-07-18 * "Waterbar" "" #trip-san-francisco-2008 Liabilities:US:Chase:Slate -57.20 USD Expenses:Food:Restaurant 57.20 USD 2008-07-18 * "Starbucks" "" #trip-san-francisco-2008 Liabilities:US:Chase:Slate -5.81 USD Expenses:Food:Coffee 5.81 USD 2008-07-18 * "Consume vacation days" Assets:US:Hooli:Vacation -64 VACHR Expenses:Vacation 64 VACHR 2008-07-18 event "location" "New Metropolis" 2008-07-19 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -30.09 USD Expenses:Food:Restaurant 30.09 USD 2008-07-21 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -16.55 USD Expenses:Food:Restaurant 16.55 USD 2008-07-25 * "Kin Soy" "Eating out with Bill" Liabilities:US:Chase:Slate -22.43 USD Expenses:Food:Restaurant 22.43 USD 2008-07-29 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -22.97 USD Expenses:Food:Restaurant 22.97 USD 2008-07-31 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2008-08-02 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -86.31 USD Expenses:Food:Groceries 86.31 USD 2008-08-03 balance Liabilities:US:Chase:Slate -895.52 USD 2008-08-03 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -72.81 USD Expenses:Food:Restaurant 72.81 USD 2008-08-04 * "Jewel of Morroco" "Eating out with Julie" Liabilities:US:Chase:Slate -26.68 USD Expenses:Food:Restaurant 26.68 USD 2008-08-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 448.92 USD Assets:US:BofA:Checking -448.92 USD 2008-08-08 * "Rose Flower" "Eating out with work buddies" Liabilities:US:Chase:Slate -51.08 USD Expenses:Food:Restaurant 51.08 USD 2008-08-10 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -33.28 USD Expenses:Food:Restaurant 33.28 USD 2008-08-11 * "China Garden" "Eating out with Julie" Liabilities:US:Chase:Slate -19.61 USD Expenses:Food:Restaurant 19.61 USD 2008-08-14 * "Uncle Boons" "Eating out after work" Liabilities:US:Chase:Slate -32.71 USD Expenses:Food:Restaurant 32.71 USD 2008-08-15 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -77.91 USD Expenses:Food:Groceries 77.91 USD 2008-08-18 * "Jewel of Morroco" "Eating out with work buddies" Liabilities:US:Chase:Slate -17.95 USD Expenses:Food:Restaurant 17.95 USD 2008-08-22 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -61.25 USD Expenses:Food:Groceries 61.25 USD 2008-08-23 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -30.81 USD Expenses:Food:Restaurant 30.81 USD 2008-08-25 balance Liabilities:US:Chase:Slate -870.69 USD 2008-08-27 * "Uncle Boons" "Eating out " Liabilities:US:Chase:Slate -30.55 USD Expenses:Food:Restaurant 30.55 USD 2008-08-30 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -29.14 USD Expenses:Food:Restaurant 29.14 USD 2008-08-30 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2008-08-31 * "China Garden" "Eating out with Natasha" Liabilities:US:Chase:Slate -38.37 USD Expenses:Food:Restaurant 38.37 USD 2008-09-03 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -57.04 USD Expenses:Food:Restaurant 57.04 USD 2008-09-03 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -75.65 USD Expenses:Food:Groceries 75.65 USD 2008-09-05 * "Cafe Modagor" "Eating out with Bill" Liabilities:US:Chase:Slate -26.96 USD Expenses:Food:Restaurant 26.96 USD 2008-09-06 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -37.86 USD Expenses:Food:Restaurant 37.86 USD 2008-09-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 723.30 USD Assets:US:BofA:Checking -723.30 USD 2008-09-11 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -34.21 USD Expenses:Food:Restaurant 34.21 USD 2008-09-12 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -49.93 USD Expenses:Food:Restaurant 49.93 USD 2008-09-13 * "Chichipotle" "Eating out after work" Liabilities:US:Chase:Slate -46.91 USD Expenses:Food:Restaurant 46.91 USD 2008-09-16 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -26.90 USD Expenses:Food:Restaurant 26.90 USD 2008-09-18 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -33.59 USD Expenses:Food:Restaurant 33.59 USD 2008-09-20 balance Liabilities:US:Chase:Slate -754.50 USD 2008-09-20 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -21.46 USD Expenses:Food:Restaurant 21.46 USD 2008-09-22 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -105.35 USD Expenses:Food:Groceries 105.35 USD 2008-09-24 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -41.15 USD Expenses:Food:Restaurant 41.15 USD 2008-09-28 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -26.77 USD Expenses:Food:Restaurant 26.77 USD 2008-09-29 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2008-10-03 * "Cafe Modagor" "Eating out with Julie" Liabilities:US:Chase:Slate -40.07 USD Expenses:Food:Restaurant 40.07 USD 2008-10-07 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -47.13 USD Expenses:Food:Restaurant 47.13 USD 2008-10-10 * "China Garden" "Eating out with Natasha" Liabilities:US:Chase:Slate -25.23 USD Expenses:Food:Restaurant 25.23 USD 2008-10-10 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -59.83 USD Expenses:Food:Groceries 59.83 USD 2008-10-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 584.49 USD Assets:US:BofA:Checking -584.49 USD 2008-10-11 * "Chichipotle" "Eating out with Natasha" Liabilities:US:Chase:Slate -48.63 USD Expenses:Food:Restaurant 48.63 USD 2008-10-14 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -21.12 USD Expenses:Food:Restaurant 21.12 USD 2008-10-15 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -55.68 USD Expenses:Food:Groceries 55.68 USD 2008-10-16 balance Liabilities:US:Chase:Slate -782.43 USD 2008-10-18 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -23.89 USD Expenses:Food:Restaurant 23.89 USD 2008-10-21 * "China Garden" "Eating out with Julie" Liabilities:US:Chase:Slate -31.00 USD Expenses:Food:Restaurant 31.00 USD 2008-10-22 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -51.45 USD Expenses:Food:Groceries 51.45 USD 2008-10-26 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -25.50 USD Expenses:Food:Restaurant 25.50 USD 2008-10-27 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -21.63 USD Expenses:Food:Restaurant 21.63 USD 2008-10-27 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -123.73 USD Expenses:Food:Groceries 123.73 USD 2008-10-27 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2008-10-29 * "Uncle Boons" "Eating out " Liabilities:US:Chase:Slate -18.08 USD Expenses:Food:Restaurant 18.08 USD 2008-10-30 * "Uncle Boons" "Eating out with Julie" Liabilities:US:Chase:Slate -24.16 USD Expenses:Food:Restaurant 24.16 USD 2008-10-31 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -64.59 USD Expenses:Food:Restaurant 64.59 USD 2008-11-02 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -29.80 USD Expenses:Food:Restaurant 29.80 USD 2008-11-06 balance Liabilities:US:Chase:Slate -1316.26 USD 2008-11-06 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -24.63 USD Expenses:Food:Restaurant 24.63 USD 2008-11-06 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -75.60 USD Expenses:Food:Groceries 75.60 USD 2008-11-08 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 845.42 USD Assets:US:BofA:Checking -845.42 USD 2008-11-10 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -26.10 USD Expenses:Food:Restaurant 26.10 USD 2008-11-15 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -27.22 USD Expenses:Food:Restaurant 27.22 USD 2008-11-16 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -112.10 USD Expenses:Food:Groceries 112.10 USD 2008-11-17 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -46.59 USD Expenses:Food:Restaurant 46.59 USD 2008-11-21 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -25.07 USD Expenses:Food:Restaurant 25.07 USD 2008-11-23 * "Cafe Modagor" "Eating out with Julie" Liabilities:US:Chase:Slate -15.93 USD Expenses:Food:Restaurant 15.93 USD 2008-11-25 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -69.77 USD Expenses:Food:Groceries 69.77 USD 2008-11-27 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -30.29 USD Expenses:Food:Restaurant 30.29 USD 2008-11-28 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -20.72 USD Expenses:Food:Restaurant 20.72 USD 2008-11-28 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2008-11-29 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -34.88 USD Expenses:Food:Restaurant 34.88 USD 2008-12-03 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -24.95 USD Expenses:Food:Restaurant 24.95 USD 2008-12-05 balance Liabilities:US:Chase:Slate -1124.69 USD 2008-12-07 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -100.21 USD Expenses:Food:Groceries 100.21 USD 2008-12-08 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -34.75 USD Expenses:Food:Restaurant 34.75 USD 2008-12-09 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -37.10 USD Expenses:Food:Restaurant 37.10 USD 2008-12-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 699.58 USD Assets:US:BofA:Checking -699.58 USD 2008-12-10 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -40.23 USD Expenses:Food:Restaurant 40.23 USD 2008-12-15 * "Chichipotle" "Eating out with Natasha" Liabilities:US:Chase:Slate -28.90 USD Expenses:Food:Restaurant 28.90 USD 2008-12-17 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -27.83 USD Expenses:Food:Restaurant 27.83 USD 2008-12-20 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -38.26 USD Expenses:Food:Restaurant 38.26 USD 2008-12-22 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -25.87 USD Expenses:Food:Restaurant 25.87 USD 2008-12-23 * "Cafe Modagor" "Eating out with Julie" Liabilities:US:Chase:Slate -35.47 USD Expenses:Food:Restaurant 35.47 USD 2008-12-27 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -28.01 USD Expenses:Food:Restaurant 28.01 USD 2008-12-27 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -60.48 USD Expenses:Food:Groceries 60.48 USD 2008-12-27 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2008-12-29 * "Rose Flower" "Eating out " Liabilities:US:Chase:Slate -20.57 USD Expenses:Food:Restaurant 20.57 USD 2008-12-31 balance Liabilities:US:Chase:Slate -1022.79 USD 2009-01-03 * "Goba Goba" "Eating out " Liabilities:US:Chase:Slate -9.79 USD Expenses:Food:Restaurant 9.79 USD 2009-01-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 466.11 USD Assets:US:BofA:Checking -466.11 USD 2009-01-08 * "Uncle Boons" "Eating out with work buddies" Liabilities:US:Chase:Slate -30.70 USD Expenses:Food:Restaurant 30.70 USD 2009-01-10 * "Rose Flower" "Eating out with work buddies" Liabilities:US:Chase:Slate -34.08 USD Expenses:Food:Restaurant 34.08 USD 2009-01-15 * "Rose Flower" "Eating out " Liabilities:US:Chase:Slate -42.42 USD Expenses:Food:Restaurant 42.42 USD 2009-01-15 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -72.02 USD Expenses:Food:Groceries 72.02 USD 2009-01-20 * "Goba Goba" "Eating out alone" Liabilities:US:Chase:Slate -27.48 USD Expenses:Food:Restaurant 27.48 USD 2009-01-20 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -58.69 USD Expenses:Food:Groceries 58.69 USD 2009-01-21 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -25.33 USD Expenses:Food:Restaurant 25.33 USD 2009-01-25 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -39.61 USD Expenses:Food:Restaurant 39.61 USD 2009-01-27 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2009-01-29 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -37.51 USD Expenses:Food:Groceries 37.51 USD 2009-01-30 balance Liabilities:US:Chase:Slate -1054.31 USD 2009-01-30 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -49.09 USD Expenses:Food:Restaurant 49.09 USD 2009-02-03 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -42.78 USD Expenses:Food:Restaurant 42.78 USD 2009-02-06 * "Kin Soy" "Eating out with work buddies" Liabilities:US:Chase:Slate -37.60 USD Expenses:Food:Restaurant 37.60 USD 2009-02-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 622.71 USD Assets:US:BofA:Checking -622.71 USD 2009-02-10 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -36.10 USD Expenses:Food:Restaurant 36.10 USD 2009-02-12 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -25.69 USD Expenses:Food:Restaurant 25.69 USD 2009-02-15 * "Chichipotle" "Eating out after work" Liabilities:US:Chase:Slate -23.37 USD Expenses:Food:Restaurant 23.37 USD 2009-02-16 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -78.25 USD Expenses:Food:Groceries 78.25 USD 2009-02-19 balance Liabilities:US:Chase:Slate -724.48 USD 2009-02-19 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -29.28 USD Expenses:Food:Restaurant 29.28 USD 2009-02-20 * "China Garden" "Eating out with Julie" Liabilities:US:Chase:Slate -15.03 USD Expenses:Food:Restaurant 15.03 USD 2009-02-21 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -32.66 USD Expenses:Food:Restaurant 32.66 USD 2009-02-24 * "China Garden" "Eating out " Liabilities:US:Chase:Slate -22.52 USD Expenses:Food:Restaurant 22.52 USD 2009-02-28 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2009-03-01 * "Kin Soy" "Eating out with Julie" Liabilities:US:Chase:Slate -49.01 USD Expenses:Food:Restaurant 49.01 USD 2009-03-02 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -43.82 USD Expenses:Food:Groceries 43.82 USD 2009-03-05 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -61.78 USD Expenses:Food:Restaurant 61.78 USD 2009-03-07 * "Goba Goba" "Eating out " Liabilities:US:Chase:Slate -40.23 USD Expenses:Food:Restaurant 40.23 USD 2009-03-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 560.64 USD Assets:US:BofA:Checking -560.64 USD 2009-03-12 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -19.00 USD Expenses:Food:Restaurant 19.00 USD 2009-03-13 balance Liabilities:US:Chase:Slate -597.17 USD 2009-03-14 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -98.03 USD Expenses:Food:Groceries 98.03 USD 2009-03-16 * "Rose Flower" "Eating out with work buddies" Liabilities:US:Chase:Slate -16.41 USD Expenses:Food:Restaurant 16.41 USD 2009-03-18 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -49.44 USD Expenses:Food:Restaurant 49.44 USD 2009-03-21 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -43.60 USD Expenses:Food:Restaurant 43.60 USD 2009-03-26 * "China Garden" "Eating out with Natasha" Liabilities:US:Chase:Slate -31.99 USD Expenses:Food:Restaurant 31.99 USD 2009-03-29 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2009-03-31 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -28.47 USD Expenses:Food:Restaurant 28.47 USD 2009-04-01 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -43.47 USD Expenses:Food:Restaurant 43.47 USD 2009-04-01 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -87.46 USD Expenses:Food:Groceries 87.46 USD 2009-04-03 balance Liabilities:US:Chase:Slate -1116.04 USD 2009-04-05 * "Kin Soy" "Eating out with work buddies" Liabilities:US:Chase:Slate -40.51 USD Expenses:Food:Restaurant 40.51 USD 2009-04-06 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -28.44 USD Expenses:Food:Restaurant 28.44 USD 2009-04-08 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -19.02 USD Expenses:Food:Restaurant 19.02 USD 2009-04-09 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -23.57 USD Expenses:Food:Restaurant 23.57 USD 2009-04-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 630.41 USD Assets:US:BofA:Checking -630.41 USD 2009-04-14 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -27.86 USD Expenses:Food:Restaurant 27.86 USD 2009-04-15 * "Rose Flower" "Eating out after work" Liabilities:US:Chase:Slate -39.93 USD Expenses:Food:Restaurant 39.93 USD 2009-04-17 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -33.83 USD Expenses:Food:Restaurant 33.83 USD 2009-04-18 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -83.98 USD Expenses:Food:Groceries 83.98 USD 2009-04-19 * "Jewel of Morroco" "Eating out with work buddies" Liabilities:US:Chase:Slate -20.19 USD Expenses:Food:Restaurant 20.19 USD 2009-04-21 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -49.29 USD Expenses:Food:Restaurant 49.29 USD 2009-04-23 * "Cafe Modagor" "Eating out with Julie" Liabilities:US:Chase:Slate -31.11 USD Expenses:Food:Restaurant 31.11 USD 2009-04-24 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -87.86 USD Expenses:Food:Groceries 87.86 USD 2009-04-27 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -23.39 USD Expenses:Food:Restaurant 23.39 USD 2009-04-28 balance Liabilities:US:Chase:Slate -994.61 USD 2009-05-01 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -22.77 USD Expenses:Food:Restaurant 22.77 USD 2009-05-01 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2009-05-05 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -14.63 USD Expenses:Food:Restaurant 14.63 USD 2009-05-06 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -106.88 USD Expenses:Food:Groceries 106.88 USD 2009-05-07 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -29.75 USD Expenses:Food:Restaurant 29.75 USD 2009-05-11 * "Uncle Boons" "Eating out with work buddies" Liabilities:US:Chase:Slate -23.73 USD Expenses:Food:Restaurant 23.73 USD 2009-05-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 715.20 USD Assets:US:BofA:Checking -715.20 USD 2009-05-15 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -36.20 USD Expenses:Food:Restaurant 36.20 USD 2009-05-18 * "China Garden" "Eating out " Liabilities:US:Chase:Slate -50.74 USD Expenses:Food:Restaurant 50.74 USD 2009-05-20 * "Uncle Boons" "Eating out with Julie" Liabilities:US:Chase:Slate -16.07 USD Expenses:Food:Restaurant 16.07 USD 2009-05-23 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -80.96 USD Expenses:Food:Groceries 80.96 USD 2009-05-24 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -36.48 USD Expenses:Food:Restaurant 36.48 USD 2009-05-25 balance Liabilities:US:Chase:Slate -817.62 USD 2009-05-25 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -28.80 USD Expenses:Food:Restaurant 28.80 USD 2009-05-26 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -53.86 USD Expenses:Food:Restaurant 53.86 USD 2009-05-27 * "Chichipotle" "Eating out with work buddies" Liabilities:US:Chase:Slate -43.97 USD Expenses:Food:Restaurant 43.97 USD 2009-06-01 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -31.76 USD Expenses:Food:Restaurant 31.76 USD 2009-06-01 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2009-06-05 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -16.87 USD Expenses:Food:Restaurant 16.87 USD 2009-06-05 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -103.82 USD Expenses:Food:Groceries 103.82 USD 2009-06-08 * "Uncle Boons" "Eating out " Liabilities:US:Chase:Slate -22.48 USD Expenses:Food:Restaurant 22.48 USD 2009-06-09 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -27.88 USD Expenses:Food:Restaurant 27.88 USD 2009-06-10 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -51.45 USD Expenses:Food:Restaurant 51.45 USD 2009-06-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 761.20 USD Assets:US:BofA:Checking -761.20 USD 2009-06-13 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -39.86 USD Expenses:Food:Restaurant 39.86 USD 2009-06-15 balance Liabilities:US:Chase:Slate -597.17 USD 2009-06-18 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -23.93 USD Expenses:Food:Restaurant 23.93 USD 2009-06-18 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -68.19 USD Expenses:Food:Groceries 68.19 USD 2009-06-21 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -17.54 USD Expenses:Food:Restaurant 17.54 USD 2009-06-22 * "Kin Soy" "Eating out with Bill" Liabilities:US:Chase:Slate -32.50 USD Expenses:Food:Restaurant 32.50 USD 2009-06-26 * "Uncle Boons" "Eating out with Julie" Liabilities:US:Chase:Slate -36.02 USD Expenses:Food:Restaurant 36.02 USD 2009-07-01 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -18.84 USD Expenses:Food:Restaurant 18.84 USD 2009-07-02 * "Cafe Modagor" "Eating out with Bill" Liabilities:US:Chase:Slate -41.96 USD Expenses:Food:Restaurant 41.96 USD 2009-07-02 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2009-07-04 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -32.96 USD Expenses:Food:Restaurant 32.96 USD 2009-07-04 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -96.48 USD Expenses:Food:Groceries 96.48 USD 2009-07-05 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -18.19 USD Expenses:Food:Restaurant 18.19 USD 2009-07-07 balance Liabilities:US:Chase:Slate -1103.78 USD 2009-07-07 * "China Garden" "Eating out " Liabilities:US:Chase:Slate -33.21 USD Expenses:Food:Restaurant 33.21 USD 2009-07-09 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -28.77 USD Expenses:Food:Restaurant 28.77 USD 2009-07-10 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -20.76 USD Expenses:Food:Restaurant 20.76 USD 2009-07-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 635.69 USD Assets:US:BofA:Checking -635.69 USD 2009-07-12 * "Uncle Boons" "Eating out after work" Liabilities:US:Chase:Slate -46.34 USD Expenses:Food:Restaurant 46.34 USD 2009-07-15 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -70.17 USD Expenses:Food:Restaurant 70.17 USD 2009-07-16 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -45.17 USD Expenses:Food:Restaurant 45.17 USD 2009-07-19 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -74.38 USD Expenses:Food:Restaurant 74.38 USD 2009-07-19 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -127.99 USD Expenses:Food:Groceries 127.99 USD 2009-07-24 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -73.24 USD Expenses:Food:Restaurant 73.24 USD 2009-07-27 balance Liabilities:US:Chase:Slate -988.12 USD 2009-07-29 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -30.53 USD Expenses:Food:Restaurant 30.53 USD 2009-07-30 * "Rose Flower" "Eating out " Liabilities:US:Chase:Slate -8.55 USD Expenses:Food:Restaurant 8.55 USD 2009-07-31 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2009-08-01 * "Jewel of Morroco" "Eating out with work buddies" Liabilities:US:Chase:Slate -14.20 USD Expenses:Food:Restaurant 14.20 USD 2009-08-06 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -23.01 USD Expenses:Food:Restaurant 23.01 USD 2009-08-07 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -19.15 USD Expenses:Food:Restaurant 19.15 USD 2009-08-07 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -106.14 USD Expenses:Food:Groceries 106.14 USD 2009-08-08 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -16.02 USD Expenses:Food:Restaurant 16.02 USD 2009-08-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 762.43 USD Assets:US:BofA:Checking -762.43 USD 2009-08-12 event "location" "New York" 2009-08-13 * "Uncle Boons" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -42.07 USD Expenses:Food:Restaurant 42.07 USD 2009-08-13 * "Cafe Select" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -27.29 USD Expenses:Food:Restaurant 27.29 USD 2009-08-13 * "Laut" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -32.28 USD Expenses:Food:Restaurant 32.28 USD 2009-08-13 * "La Colombe" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -5.99 USD Expenses:Food:Coffee 5.99 USD 2009-08-14 * "Cafe Select" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -25.72 USD Expenses:Food:Restaurant 25.72 USD 2009-08-14 * "La Colombe" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -6.75 USD Expenses:Food:Coffee 6.75 USD 2009-08-15 * "Uncle Boons" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -44.23 USD Expenses:Food:Restaurant 44.23 USD 2009-08-15 * "La Colombe" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -5.16 USD Expenses:Food:Coffee 5.16 USD 2009-08-16 * "Takahachi" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -52.84 USD Expenses:Food:Restaurant 52.84 USD 2009-08-16 * "La Colombe" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -4.09 USD Expenses:Food:Coffee 4.09 USD 2009-08-17 balance Liabilities:US:Chase:Slate -809.71 USD 2009-08-18 * "Uncle Boons" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -43.80 USD Expenses:Food:Restaurant 43.80 USD 2009-08-18 * "Laut" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -27.52 USD Expenses:Food:Restaurant 27.52 USD 2009-08-19 * "Cafe Select" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -31.47 USD Expenses:Food:Restaurant 31.47 USD 2009-08-19 * "Takahachi" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -57.22 USD Expenses:Food:Restaurant 57.22 USD 2009-08-19 * "Laut" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -32.61 USD Expenses:Food:Restaurant 32.61 USD 2009-08-19 * "Gimme! Coffee" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -5.29 USD Expenses:Food:Coffee 5.29 USD 2009-08-20 * "Cafe Select" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -32.39 USD Expenses:Food:Restaurant 32.39 USD 2009-08-20 * "Gimme! Coffee" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -6.52 USD Expenses:Food:Coffee 6.52 USD 2009-08-21 * "Gimme! Coffee" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -7.49 USD Expenses:Food:Coffee 7.49 USD 2009-08-22 * "Cafe Select" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -29.98 USD Expenses:Food:Restaurant 29.98 USD 2009-08-22 * "La Colombe" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -7.50 USD Expenses:Food:Coffee 7.50 USD 2009-08-22 * "Gimme! Coffee" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -6.26 USD Expenses:Food:Coffee 6.26 USD 2009-08-23 * "Uncle Boons" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -39.54 USD Expenses:Food:Restaurant 39.54 USD 2009-08-23 * "Gimme! Coffee" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -8.08 USD Expenses:Food:Coffee 8.08 USD 2009-08-25 * "Takahachi" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -52.71 USD Expenses:Food:Restaurant 52.71 USD 2009-08-26 * "Laut" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -30.25 USD Expenses:Food:Restaurant 30.25 USD 2009-08-26 * "La Colombe" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -6.11 USD Expenses:Food:Coffee 6.11 USD 2009-08-27 * "Cafe Select" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -32.96 USD Expenses:Food:Restaurant 32.96 USD 2009-08-27 * "Takahachi" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -53.19 USD Expenses:Food:Restaurant 53.19 USD 2009-08-27 * "La Colombe" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -7.63 USD Expenses:Food:Coffee 7.63 USD 2009-08-28 * "Uncle Boons" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -37.99 USD Expenses:Food:Restaurant 37.99 USD 2009-08-28 * "Takahachi" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -52.38 USD Expenses:Food:Restaurant 52.38 USD 2009-08-29 * "Uncle Boons" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -44.90 USD Expenses:Food:Restaurant 44.90 USD 2009-08-30 * "Takahachi" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -47.72 USD Expenses:Food:Restaurant 47.72 USD 2009-08-31 * "Cafe Select" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -32.13 USD Expenses:Food:Restaurant 32.13 USD 2009-08-31 * "Takahachi" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -51.91 USD Expenses:Food:Restaurant 51.91 USD 2009-08-31 * "Gimme! Coffee" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -6.75 USD Expenses:Food:Coffee 6.75 USD 2009-09-01 * "Takahachi" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -49.34 USD Expenses:Food:Restaurant 49.34 USD 2009-09-02 * "Gimme! Coffee" "" #trip-new-york-2009 Liabilities:US:Chase:Slate -5.77 USD Expenses:Food:Coffee 5.77 USD 2009-09-02 * "Consume vacation days" Assets:US:Hooli:Vacation -168 VACHR Expenses:Vacation 168 VACHR 2009-09-02 event "location" "New Metropolis" 2009-09-04 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -33.88 USD Expenses:Food:Restaurant 33.88 USD 2009-09-06 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -26.95 USD Expenses:Food:Restaurant 26.95 USD 2009-09-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 62.58 USD Assets:US:BofA:Checking -62.58 USD 2009-09-08 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -35.63 USD Expenses:Food:Restaurant 35.63 USD 2009-09-09 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -82.42 USD Expenses:Food:Groceries 82.42 USD 2009-09-13 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -29.04 USD Expenses:Food:Restaurant 29.04 USD 2009-09-15 balance Liabilities:US:Chase:Slate -1802.46 USD 2009-09-15 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -18.85 USD Expenses:Food:Restaurant 18.85 USD 2009-09-17 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -140.08 USD Expenses:Food:Groceries 140.08 USD 2009-09-20 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -47.36 USD Expenses:Food:Restaurant 47.36 USD 2009-09-22 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -47.69 USD Expenses:Food:Restaurant 47.69 USD 2009-09-24 * "Goba Goba" "Eating out alone" Liabilities:US:Chase:Slate -23.68 USD Expenses:Food:Restaurant 23.68 USD 2009-09-25 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2009-09-27 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -29.91 USD Expenses:Food:Restaurant 29.91 USD 2009-10-01 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -11.09 USD Expenses:Food:Restaurant 11.09 USD 2009-10-02 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -80.65 USD Expenses:Food:Groceries 80.65 USD 2009-10-04 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -44.63 USD Expenses:Food:Restaurant 44.63 USD 2009-10-08 * "Rose Flower" "Eating out " Liabilities:US:Chase:Slate -67.44 USD Expenses:Food:Restaurant 67.44 USD 2009-10-09 balance Liabilities:US:Chase:Slate -2433.84 USD 2009-10-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 782.50 USD Assets:US:BofA:Checking -782.50 USD 2009-10-13 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -39.66 USD Expenses:Food:Restaurant 39.66 USD 2009-10-16 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -35.67 USD Expenses:Food:Restaurant 35.67 USD 2009-10-17 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -65.19 USD Expenses:Food:Groceries 65.19 USD 2009-10-20 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -28.96 USD Expenses:Food:Restaurant 28.96 USD 2009-10-21 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -25.03 USD Expenses:Food:Restaurant 25.03 USD 2009-10-22 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -63.80 USD Expenses:Food:Groceries 63.80 USD 2009-10-23 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -34.97 USD Expenses:Food:Restaurant 34.97 USD 2009-10-23 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2009-10-28 * "Kin Soy" "Eating out with Julie" Liabilities:US:Chase:Slate -21.08 USD Expenses:Food:Restaurant 21.08 USD 2009-10-29 * "China Garden" "Eating out with Bill" Liabilities:US:Chase:Slate -15.79 USD Expenses:Food:Restaurant 15.79 USD 2009-10-30 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -29.39 USD Expenses:Food:Restaurant 29.39 USD 2009-11-02 * "Kin Soy" "Eating out with Bill" Liabilities:US:Chase:Slate -12.87 USD Expenses:Food:Restaurant 12.87 USD 2009-11-03 * "Kin Soy" "Eating out with Julie" Liabilities:US:Chase:Slate -29.24 USD Expenses:Food:Restaurant 29.24 USD 2009-11-04 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -50.22 USD Expenses:Food:Restaurant 50.22 USD 2009-11-05 balance Liabilities:US:Chase:Slate -2223.21 USD 2009-11-05 * "Kin Soy" "Eating out with Julie" Liabilities:US:Chase:Slate -18.61 USD Expenses:Food:Restaurant 18.61 USD 2009-11-07 * "Kin Soy" "Eating out with Julie" Liabilities:US:Chase:Slate -54.59 USD Expenses:Food:Restaurant 54.59 USD 2009-11-08 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 656.00 USD Assets:US:BofA:Checking -656.00 USD 2009-11-10 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -50.59 USD Expenses:Food:Groceries 50.59 USD 2009-11-12 * "Uncle Boons" "Eating out with Julie" Liabilities:US:Chase:Slate -19.70 USD Expenses:Food:Restaurant 19.70 USD 2009-11-15 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -50.36 USD Expenses:Food:Groceries 50.36 USD 2009-11-16 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -34.41 USD Expenses:Food:Restaurant 34.41 USD 2009-11-18 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -52.64 USD Expenses:Food:Restaurant 52.64 USD 2009-11-23 * "Goba Goba" "Eating out " Liabilities:US:Chase:Slate -39.55 USD Expenses:Food:Restaurant 39.55 USD 2009-11-23 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2009-11-24 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -128.07 USD Expenses:Food:Groceries 128.07 USD 2009-11-27 * "Jewel of Morroco" "Eating out with Julie" Liabilities:US:Chase:Slate -38.43 USD Expenses:Food:Restaurant 38.43 USD 2009-11-29 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -53.32 USD Expenses:Food:Restaurant 53.32 USD 2009-11-30 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -39.23 USD Expenses:Food:Restaurant 39.23 USD 2009-12-01 balance Liabilities:US:Chase:Slate -2266.71 USD 2009-12-03 * "Jewel of Morroco" "Eating out with Natasha" Liabilities:US:Chase:Slate -34.35 USD Expenses:Food:Restaurant 34.35 USD 2009-12-05 * "Jewel of Morroco" "Eating out with work buddies" Liabilities:US:Chase:Slate -57.40 USD Expenses:Food:Restaurant 57.40 USD 2009-12-07 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -99.85 USD Expenses:Food:Groceries 99.85 USD 2009-12-10 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -23.19 USD Expenses:Food:Restaurant 23.19 USD 2009-12-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 896.24 USD Assets:US:BofA:Checking -896.24 USD 2009-12-12 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -105.74 USD Expenses:Food:Groceries 105.74 USD 2009-12-15 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -29.07 USD Expenses:Food:Restaurant 29.07 USD 2009-12-16 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -35.61 USD Expenses:Food:Restaurant 35.61 USD 2009-12-17 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -36.45 USD Expenses:Food:Restaurant 36.45 USD 2009-12-21 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -71.15 USD Expenses:Food:Restaurant 71.15 USD 2009-12-22 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -35.13 USD Expenses:Food:Restaurant 35.13 USD 2009-12-25 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -35.47 USD Expenses:Food:Restaurant 35.47 USD 2009-12-25 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2009-12-26 * "China Garden" "Eating out with Bill" Liabilities:US:Chase:Slate -26.02 USD Expenses:Food:Restaurant 26.02 USD 2009-12-27 * "Chichipotle" "Eating out with work buddies" Liabilities:US:Chase:Slate -33.74 USD Expenses:Food:Restaurant 33.74 USD 2009-12-30 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -31.88 USD Expenses:Food:Restaurant 31.88 USD 2009-12-31 balance Liabilities:US:Chase:Slate -2145.52 USD 2009-12-31 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -74.21 USD Expenses:Food:Groceries 74.21 USD 2010-01-02 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -25.29 USD Expenses:Food:Restaurant 25.29 USD 2010-01-03 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -84.82 USD Expenses:Food:Restaurant 84.82 USD 2010-01-06 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -18.49 USD Expenses:Food:Restaurant 18.49 USD 2010-01-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 681.76 USD Assets:US:BofA:Checking -681.76 USD 2010-01-11 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -24.43 USD Expenses:Food:Restaurant 24.43 USD 2010-01-16 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -29.19 USD Expenses:Food:Restaurant 29.19 USD 2010-01-17 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -69.67 USD Expenses:Food:Groceries 69.67 USD 2010-01-18 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -22.29 USD Expenses:Food:Restaurant 22.29 USD 2010-01-19 * "Uncle Boons" "Eating out with Bill" Liabilities:US:Chase:Slate -41.59 USD Expenses:Food:Restaurant 41.59 USD 2010-01-21 balance Liabilities:US:Chase:Slate -1853.74 USD 2010-01-22 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -19.65 USD Expenses:Food:Restaurant 19.65 USD 2010-01-22 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2010-01-26 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -25.04 USD Expenses:Food:Restaurant 25.04 USD 2010-01-28 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -52.43 USD Expenses:Food:Groceries 52.43 USD 2010-01-31 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -34.61 USD Expenses:Food:Restaurant 34.61 USD 2010-02-05 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -21.13 USD Expenses:Food:Restaurant 21.13 USD 2010-02-07 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -80.61 USD Expenses:Food:Groceries 80.61 USD 2010-02-09 * "Jewel of Morroco" "Eating out with Julie" Liabilities:US:Chase:Slate -27.48 USD Expenses:Food:Restaurant 27.48 USD 2010-02-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 568.64 USD Assets:US:BofA:Checking -568.64 USD 2010-02-13 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -24.95 USD Expenses:Food:Restaurant 24.95 USD 2010-02-14 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -16.75 USD Expenses:Food:Restaurant 16.75 USD 2010-02-17 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -14.74 USD Expenses:Food:Restaurant 14.74 USD 2010-02-19 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -52.22 USD Expenses:Food:Restaurant 52.22 USD 2010-02-20 balance Liabilities:US:Chase:Slate -1774.71 USD 2010-02-22 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -76.98 USD Expenses:Food:Groceries 76.98 USD 2010-02-23 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2010-02-24 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -15.74 USD Expenses:Food:Restaurant 15.74 USD 2010-03-01 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -27.31 USD Expenses:Food:Restaurant 27.31 USD 2010-03-04 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -15.25 USD Expenses:Food:Restaurant 15.25 USD 2010-03-05 * "Jewel of Morroco" "Eating out with work buddies" Liabilities:US:Chase:Slate -12.15 USD Expenses:Food:Restaurant 12.15 USD 2010-03-06 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -87.93 USD Expenses:Food:Groceries 87.93 USD 2010-03-09 * "Uncle Boons" "Eating out after work" Liabilities:US:Chase:Slate -29.40 USD Expenses:Food:Restaurant 29.40 USD 2010-03-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 495.31 USD Assets:US:BofA:Checking -495.31 USD 2010-03-12 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -26.84 USD Expenses:Food:Restaurant 26.84 USD 2010-03-13 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -61.55 USD Expenses:Food:Groceries 61.55 USD 2010-03-14 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -14.16 USD Expenses:Food:Restaurant 14.16 USD 2010-03-15 * "Rose Flower" "Eating out " Liabilities:US:Chase:Slate -12.76 USD Expenses:Food:Restaurant 12.76 USD 2010-03-17 * "Jewel of Morroco" "Eating out with Julie" Liabilities:US:Chase:Slate -20.28 USD Expenses:Food:Restaurant 20.28 USD 2010-03-19 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -35.90 USD Expenses:Food:Restaurant 35.90 USD 2010-03-22 balance Liabilities:US:Chase:Slate -1835.65 USD 2010-03-22 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -44.72 USD Expenses:Food:Restaurant 44.72 USD 2010-03-26 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2010-03-27 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -28.57 USD Expenses:Food:Restaurant 28.57 USD 2010-03-29 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -14.85 USD Expenses:Food:Restaurant 14.85 USD 2010-03-31 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -104.40 USD Expenses:Food:Groceries 104.40 USD 2010-04-01 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -19.09 USD Expenses:Food:Restaurant 19.09 USD 2010-04-04 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -39.10 USD Expenses:Food:Restaurant 39.10 USD 2010-04-09 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -18.12 USD Expenses:Food:Restaurant 18.12 USD 2010-04-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 533.50 USD Assets:US:BofA:Checking -533.50 USD 2010-04-10 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -31.09 USD Expenses:Food:Restaurant 31.09 USD 2010-04-11 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -18.50 USD Expenses:Food:Restaurant 18.50 USD 2010-04-14 balance Liabilities:US:Chase:Slate -1740.59 USD 2010-04-16 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -44.15 USD Expenses:Food:Restaurant 44.15 USD 2010-04-17 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -111.41 USD Expenses:Food:Groceries 111.41 USD 2010-04-20 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -39.12 USD Expenses:Food:Restaurant 39.12 USD 2010-04-23 * "Kin Soy" "Eating out after work" Liabilities:US:Chase:Slate -24.53 USD Expenses:Food:Restaurant 24.53 USD 2010-04-27 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2010-04-28 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -24.95 USD Expenses:Food:Restaurant 24.95 USD 2010-04-30 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -11.43 USD Expenses:Food:Restaurant 11.43 USD 2010-05-01 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -108.82 USD Expenses:Food:Groceries 108.82 USD 2010-05-03 * "Jewel of Morroco" "Eating out with Natasha" Liabilities:US:Chase:Slate -27.23 USD Expenses:Food:Restaurant 27.23 USD 2010-05-07 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -31.90 USD Expenses:Food:Restaurant 31.90 USD 2010-05-08 balance Liabilities:US:Chase:Slate -2284.13 USD 2010-05-09 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -31.78 USD Expenses:Food:Restaurant 31.78 USD 2010-05-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 624.91 USD Assets:US:BofA:Checking -624.91 USD 2010-05-10 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -61.65 USD Expenses:Food:Groceries 61.65 USD 2010-05-11 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -15.10 USD Expenses:Food:Restaurant 15.10 USD 2010-05-16 * "China Garden" "Eating out " Liabilities:US:Chase:Slate -32.46 USD Expenses:Food:Restaurant 32.46 USD 2010-05-21 * "Cafe Modagor" "Eating out with Julie" Liabilities:US:Chase:Slate -20.52 USD Expenses:Food:Restaurant 20.52 USD 2010-05-24 * "China Garden" "Eating out with Natasha" Liabilities:US:Chase:Slate -28.44 USD Expenses:Food:Restaurant 28.44 USD 2010-05-25 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -14.35 USD Expenses:Food:Restaurant 14.35 USD 2010-05-26 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2010-05-27 event "location" "Boston" 2010-05-28 * "Giacomo's Restaurant" "" #trip-boston-2010 Liabilities:US:Chase:Slate -42.26 USD Expenses:Food:Restaurant 42.26 USD 2010-05-29 * "Franklin Cafe" "" #trip-boston-2010 Liabilities:US:Chase:Slate -26.40 USD Expenses:Food:Restaurant 26.40 USD 2010-05-30 * "Giacomo's Restaurant" "" #trip-boston-2010 Liabilities:US:Chase:Slate -44.30 USD Expenses:Food:Restaurant 44.30 USD 2010-05-30 * "Legal Seafood" "" #trip-boston-2010 Liabilities:US:Chase:Slate -34.86 USD Expenses:Food:Restaurant 34.86 USD 2010-05-30 * "Starbucks" "" #trip-boston-2010 Liabilities:US:Chase:Slate -5.51 USD Expenses:Food:Coffee 5.51 USD 2010-06-03 * "Legal Seafood" "" #trip-boston-2010 Liabilities:US:Chase:Slate -45.55 USD Expenses:Food:Restaurant 45.55 USD 2010-06-03 * "Franklin Cafe" "" #trip-boston-2010 Liabilities:US:Chase:Slate -30.74 USD Expenses:Food:Restaurant 30.74 USD 2010-06-03 * "Starbucks" "" #trip-boston-2010 Liabilities:US:Chase:Slate -6.44 USD Expenses:Food:Coffee 6.44 USD 2010-06-04 balance Liabilities:US:Chase:Slate -2219.58 USD 2010-06-04 * "Franklin Cafe" "" #trip-boston-2010 Liabilities:US:Chase:Slate -28.65 USD Expenses:Food:Restaurant 28.65 USD 2010-06-04 * "Starbucks" "" #trip-boston-2010 Liabilities:US:Chase:Slate -5.44 USD Expenses:Food:Coffee 5.44 USD 2010-06-05 * "Franklin Cafe" "" #trip-boston-2010 Liabilities:US:Chase:Slate -25.72 USD Expenses:Food:Restaurant 25.72 USD 2010-06-05 * "Starbucks" "" #trip-boston-2010 Liabilities:US:Chase:Slate -6.92 USD Expenses:Food:Coffee 6.92 USD 2010-06-06 * "Giacomo's Restaurant" "" #trip-boston-2010 Liabilities:US:Chase:Slate -43.78 USD Expenses:Food:Restaurant 43.78 USD 2010-06-07 * "Franklin Cafe" "" #trip-boston-2010 Liabilities:US:Chase:Slate -32.32 USD Expenses:Food:Restaurant 32.32 USD 2010-06-08 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -24.65 USD Expenses:Food:Restaurant 24.65 USD 2010-06-08 * "Legal Seafood" "" #trip-boston-2010 Liabilities:US:Chase:Slate -28.70 USD Expenses:Food:Restaurant 28.70 USD 2010-06-08 * "Franklin Cafe" "" #trip-boston-2010 Liabilities:US:Chase:Slate -28.86 USD Expenses:Food:Restaurant 28.86 USD 2010-06-08 * "Consume vacation days" Assets:US:Hooli:Vacation -96 VACHR Expenses:Vacation 96 VACHR 2010-06-08 event "location" "New Metropolis" 2010-06-10 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -27.09 USD Expenses:Food:Restaurant 27.09 USD 2010-06-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 344.26 USD Assets:US:BofA:Checking -344.26 USD 2010-06-11 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -32.15 USD Expenses:Food:Restaurant 32.15 USD 2010-06-12 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -68.94 USD Expenses:Food:Groceries 68.94 USD 2010-06-16 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -32.86 USD Expenses:Food:Restaurant 32.86 USD 2010-06-21 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -21.46 USD Expenses:Food:Restaurant 21.46 USD 2010-06-22 * "China Garden" "Eating out with work buddies" Liabilities:US:Chase:Slate -37.39 USD Expenses:Food:Restaurant 37.39 USD 2010-06-24 * "Uncle Boons" "Eating out with Julie" Liabilities:US:Chase:Slate -29.84 USD Expenses:Food:Restaurant 29.84 USD 2010-06-24 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -119.68 USD Expenses:Food:Groceries 119.68 USD 2010-06-28 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -19.48 USD Expenses:Food:Restaurant 19.48 USD 2010-06-28 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2010-06-29 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -83.15 USD Expenses:Food:Groceries 83.15 USD 2010-06-30 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -26.14 USD Expenses:Food:Restaurant 26.14 USD 2010-07-01 balance Liabilities:US:Chase:Slate -2718.54 USD 2010-07-03 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -15.94 USD Expenses:Food:Restaurant 15.94 USD 2010-07-05 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -16.67 USD Expenses:Food:Restaurant 16.67 USD 2010-07-06 * "Goba Goba" "Eating out " Liabilities:US:Chase:Slate -25.19 USD Expenses:Food:Restaurant 25.19 USD 2010-07-10 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -46.93 USD Expenses:Food:Groceries 46.93 USD 2010-07-11 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -33.03 USD Expenses:Food:Restaurant 33.03 USD 2010-07-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 728.85 USD Assets:US:BofA:Checking -728.85 USD 2010-07-14 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -30.06 USD Expenses:Food:Restaurant 30.06 USD 2010-07-16 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -10.22 USD Expenses:Food:Restaurant 10.22 USD 2010-07-18 * "Rose Flower" "Eating out with work buddies" Liabilities:US:Chase:Slate -15.34 USD Expenses:Food:Restaurant 15.34 USD 2010-07-19 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -31.54 USD Expenses:Food:Restaurant 31.54 USD 2010-07-22 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -43.28 USD Expenses:Food:Restaurant 43.28 USD 2010-07-23 * "Jewel of Morroco" "Eating out with Julie" Liabilities:US:Chase:Slate -66.40 USD Expenses:Food:Restaurant 66.40 USD 2010-07-26 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -66.60 USD Expenses:Food:Groceries 66.60 USD 2010-07-28 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -15.19 USD Expenses:Food:Restaurant 15.19 USD 2010-07-28 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2010-07-30 balance Liabilities:US:Chase:Slate -2526.08 USD 2010-07-31 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -67.34 USD Expenses:Food:Groceries 67.34 USD 2010-08-01 * "Goba Goba" "Eating out " Liabilities:US:Chase:Slate -54.30 USD Expenses:Food:Restaurant 54.30 USD 2010-08-03 * "China Garden" "Eating out with Julie" Liabilities:US:Chase:Slate -43.27 USD Expenses:Food:Restaurant 43.27 USD 2010-08-07 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -33.66 USD Expenses:Food:Restaurant 33.66 USD 2010-08-08 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -70.95 USD Expenses:Food:Groceries 70.95 USD 2010-08-09 * "Chichipotle" "Eating out with work buddies" Liabilities:US:Chase:Slate -33.09 USD Expenses:Food:Restaurant 33.09 USD 2010-08-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 729.40 USD Assets:US:BofA:Checking -729.40 USD 2010-08-13 * "China Garden" "Eating out with Bill" Liabilities:US:Chase:Slate -28.16 USD Expenses:Food:Restaurant 28.16 USD 2010-08-14 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -73.09 USD Expenses:Food:Groceries 73.09 USD 2010-08-18 * "Goba Goba" "Eating out alone" Liabilities:US:Chase:Slate -25.27 USD Expenses:Food:Restaurant 25.27 USD 2010-08-19 * "China Garden" "Eating out with work buddies" Liabilities:US:Chase:Slate -48.56 USD Expenses:Food:Restaurant 48.56 USD 2010-08-22 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -29.31 USD Expenses:Food:Restaurant 29.31 USD 2010-08-23 * "Chichipotle" "Eating out after work" Liabilities:US:Chase:Slate -17.85 USD Expenses:Food:Restaurant 17.85 USD 2010-08-25 balance Liabilities:US:Chase:Slate -2321.53 USD 2010-08-26 * "Chichipotle" "Eating out with Natasha" Liabilities:US:Chase:Slate -23.24 USD Expenses:Food:Restaurant 23.24 USD 2010-08-27 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2010-08-28 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -29.65 USD Expenses:Food:Restaurant 29.65 USD 2010-08-29 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -91.43 USD Expenses:Food:Groceries 91.43 USD 2010-09-02 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -24.15 USD Expenses:Food:Restaurant 24.15 USD 2010-09-05 * "Cafe Modagor" "Eating out with Bill" Liabilities:US:Chase:Slate -26.88 USD Expenses:Food:Restaurant 26.88 USD 2010-09-06 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -100.51 USD Expenses:Food:Groceries 100.51 USD 2010-09-09 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -26.68 USD Expenses:Food:Restaurant 26.68 USD 2010-09-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 636.62 USD Assets:US:BofA:Checking -636.62 USD 2010-09-11 * "Rose Flower" "Eating out " Liabilities:US:Chase:Slate -35.88 USD Expenses:Food:Restaurant 35.88 USD 2010-09-12 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -32.99 USD Expenses:Food:Restaurant 32.99 USD 2010-09-14 balance Liabilities:US:Chase:Slate -2196.32 USD 2010-09-14 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -69.11 USD Expenses:Food:Groceries 69.11 USD 2010-09-16 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -35.42 USD Expenses:Food:Restaurant 35.42 USD 2010-09-20 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -48.28 USD Expenses:Food:Restaurant 48.28 USD 2010-09-23 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -20.45 USD Expenses:Food:Restaurant 20.45 USD 2010-09-25 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -75.46 USD Expenses:Food:Groceries 75.46 USD 2010-09-26 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -23.74 USD Expenses:Food:Restaurant 23.74 USD 2010-09-26 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2010-09-28 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -37.54 USD Expenses:Food:Restaurant 37.54 USD 2010-10-01 * "Chichipotle" "Eating out with Natasha" Liabilities:US:Chase:Slate -30.43 USD Expenses:Food:Restaurant 30.43 USD 2010-10-04 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -50.36 USD Expenses:Food:Restaurant 50.36 USD 2010-10-07 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -96.91 USD Expenses:Food:Groceries 96.91 USD 2010-10-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 676.57 USD Assets:US:BofA:Checking -676.57 USD 2010-10-09 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -22.59 USD Expenses:Food:Restaurant 22.59 USD 2010-10-11 * "Chichipotle" "Eating out after work" Liabilities:US:Chase:Slate -34.25 USD Expenses:Food:Restaurant 34.25 USD 2010-10-12 balance Liabilities:US:Chase:Slate -2184.29 USD 2010-10-16 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -52.05 USD Expenses:Food:Restaurant 52.05 USD 2010-10-19 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -37.95 USD Expenses:Food:Restaurant 37.95 USD 2010-10-20 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -86.26 USD Expenses:Food:Restaurant 86.26 USD 2010-10-20 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -65.13 USD Expenses:Food:Groceries 65.13 USD 2010-10-25 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -29.99 USD Expenses:Food:Restaurant 29.99 USD 2010-10-25 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -63.11 USD Expenses:Food:Groceries 63.11 USD 2010-10-27 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -39.52 USD Expenses:Food:Restaurant 39.52 USD 2010-10-29 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2010-10-31 * "China Garden" "Eating out with Natasha" Liabilities:US:Chase:Slate -27.35 USD Expenses:Food:Restaurant 27.35 USD 2010-11-03 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -37.44 USD Expenses:Food:Restaurant 37.44 USD 2010-11-08 balance Liabilities:US:Chase:Slate -2743.09 USD 2010-11-08 * "Jewel of Morroco" "Eating out with Julie" Liabilities:US:Chase:Slate -36.99 USD Expenses:Food:Restaurant 36.99 USD 2010-11-09 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -30.40 USD Expenses:Food:Restaurant 30.40 USD 2010-11-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 714.13 USD Assets:US:BofA:Checking -714.13 USD 2010-11-14 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -31.10 USD Expenses:Food:Restaurant 31.10 USD 2010-11-14 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -84.49 USD Expenses:Food:Groceries 84.49 USD 2010-11-19 * "China Garden" "Eating out with Natasha" Liabilities:US:Chase:Slate -41.56 USD Expenses:Food:Restaurant 41.56 USD 2010-11-20 * "Kin Soy" "Eating out after work" Liabilities:US:Chase:Slate -18.63 USD Expenses:Food:Restaurant 18.63 USD 2010-11-21 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -12.50 USD Expenses:Food:Restaurant 12.50 USD 2010-11-22 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -32.80 USD Expenses:Food:Restaurant 32.80 USD 2010-11-25 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -21.22 USD Expenses:Food:Restaurant 21.22 USD 2010-11-27 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -31.94 USD Expenses:Food:Restaurant 31.94 USD 2010-11-27 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -85.99 USD Expenses:Food:Groceries 85.99 USD 2010-12-01 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2010-12-02 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -21.48 USD Expenses:Food:Restaurant 21.48 USD 2010-12-06 * "Rose Flower" "Eating out after work" Liabilities:US:Chase:Slate -42.41 USD Expenses:Food:Restaurant 42.41 USD 2010-12-07 balance Liabilities:US:Chase:Slate -2640.47 USD 2010-12-07 * "Kin Soy" "Eating out after work" Liabilities:US:Chase:Slate -15.57 USD Expenses:Food:Restaurant 15.57 USD 2010-12-08 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -108.64 USD Expenses:Food:Groceries 108.64 USD 2010-12-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 656.37 USD Assets:US:BofA:Checking -656.37 USD 2010-12-12 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -19.14 USD Expenses:Food:Restaurant 19.14 USD 2010-12-15 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -21.86 USD Expenses:Food:Restaurant 21.86 USD 2010-12-18 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -30.05 USD Expenses:Food:Restaurant 30.05 USD 2010-12-20 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -39.75 USD Expenses:Food:Restaurant 39.75 USD 2010-12-21 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -26.60 USD Expenses:Food:Restaurant 26.60 USD 2010-12-25 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -25.92 USD Expenses:Food:Restaurant 25.92 USD 2010-12-28 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -48.09 USD Expenses:Food:Restaurant 48.09 USD 2010-12-28 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -89.36 USD Expenses:Food:Groceries 89.36 USD 2010-12-29 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2010-12-31 * "Kin Soy" "Eating out with work buddies" Liabilities:US:Chase:Slate -23.95 USD Expenses:Food:Restaurant 23.95 USD 2011-01-03 balance Liabilities:US:Chase:Slate -2553.03 USD 2011-01-05 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -30.46 USD Expenses:Food:Restaurant 30.46 USD 2011-01-08 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -40.31 USD Expenses:Food:Restaurant 40.31 USD 2011-01-11 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -22.67 USD Expenses:Food:Restaurant 22.67 USD 2011-01-11 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -57.15 USD Expenses:Food:Groceries 57.15 USD 2011-01-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 519.02 USD Assets:US:BofA:Checking -519.02 USD 2011-01-16 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -50.01 USD Expenses:Food:Restaurant 50.01 USD 2011-01-20 * "Chichipotle" "Eating out with work buddies" Liabilities:US:Chase:Slate -55.46 USD Expenses:Food:Restaurant 55.46 USD 2011-01-21 * "Chichipotle" "Eating out with Natasha" Liabilities:US:Chase:Slate -25.79 USD Expenses:Food:Restaurant 25.79 USD 2011-01-25 balance Liabilities:US:Chase:Slate -2315.86 USD 2011-01-25 * "Uncle Boons" "Eating out with Julie" Liabilities:US:Chase:Slate -19.20 USD Expenses:Food:Restaurant 19.20 USD 2011-01-26 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2011-01-27 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -28.06 USD Expenses:Food:Restaurant 28.06 USD 2011-01-29 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -124.47 USD Expenses:Food:Groceries 124.47 USD 2011-01-30 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -16.85 USD Expenses:Food:Restaurant 16.85 USD 2011-02-02 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -23.11 USD Expenses:Food:Restaurant 23.11 USD 2011-02-03 * "China Garden" "Eating out with Julie" Liabilities:US:Chase:Slate -39.24 USD Expenses:Food:Restaurant 39.24 USD 2011-02-06 * "Uncle Boons" "Eating out after work" Liabilities:US:Chase:Slate -44.89 USD Expenses:Food:Restaurant 44.89 USD 2011-02-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 627.37 USD Assets:US:BofA:Checking -627.37 USD 2011-02-08 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -23.14 USD Expenses:Food:Restaurant 23.14 USD 2011-02-08 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -105.86 USD Expenses:Food:Groceries 105.86 USD 2011-02-09 * "Jewel of Morroco" "Eating out with Natasha" Liabilities:US:Chase:Slate -58.57 USD Expenses:Food:Restaurant 58.57 USD 2011-02-10 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -18.35 USD Expenses:Food:Restaurant 18.35 USD 2011-02-15 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -24.15 USD Expenses:Food:Restaurant 24.15 USD 2011-02-17 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -11.31 USD Expenses:Food:Restaurant 11.31 USD 2011-02-19 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -10.89 USD Expenses:Food:Restaurant 10.89 USD 2011-02-20 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -27.29 USD Expenses:Food:Restaurant 27.29 USD 2011-02-21 * "China Garden" "Eating out " Liabilities:US:Chase:Slate -22.73 USD Expenses:Food:Restaurant 22.73 USD 2011-02-22 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -49.82 USD Expenses:Food:Restaurant 49.82 USD 2011-02-23 balance Liabilities:US:Chase:Slate -2456.42 USD 2011-02-25 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -75.50 USD Expenses:Food:Restaurant 75.50 USD 2011-02-25 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -77.45 USD Expenses:Food:Groceries 77.45 USD 2011-02-25 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2011-02-26 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -21.21 USD Expenses:Food:Restaurant 21.21 USD 2011-03-02 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -18.73 USD Expenses:Food:Restaurant 18.73 USD 2011-03-07 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -31.34 USD Expenses:Food:Restaurant 31.34 USD 2011-03-08 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -28.65 USD Expenses:Food:Restaurant 28.65 USD 2011-03-09 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -101.73 USD Expenses:Food:Groceries 101.73 USD 2011-03-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 824.41 USD Assets:US:BofA:Checking -824.41 USD 2011-03-13 * "Uncle Boons" "Eating out with Julie" Liabilities:US:Chase:Slate -20.83 USD Expenses:Food:Restaurant 20.83 USD 2011-03-14 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -20.83 USD Expenses:Food:Restaurant 20.83 USD 2011-03-15 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -30.27 USD Expenses:Food:Restaurant 30.27 USD 2011-03-17 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -32.97 USD Expenses:Food:Restaurant 32.97 USD 2011-03-22 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -35.10 USD Expenses:Food:Restaurant 35.10 USD 2011-03-22 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -50.05 USD Expenses:Food:Groceries 50.05 USD 2011-03-24 balance Liabilities:US:Chase:Slate -2296.67 USD 2011-03-24 * "Uncle Boons" "Eating out with Bill" Liabilities:US:Chase:Slate -43.65 USD Expenses:Food:Restaurant 43.65 USD 2011-03-26 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2011-03-28 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -36.43 USD Expenses:Food:Restaurant 36.43 USD 2011-03-31 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -21.14 USD Expenses:Food:Restaurant 21.14 USD 2011-04-01 * "Uncle Boons" "Eating out after work" Liabilities:US:Chase:Slate -51.51 USD Expenses:Food:Restaurant 51.51 USD 2011-04-02 event "location" "San Francisco" 2011-04-03 * "Bar Crudo" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -73.28 USD Expenses:Food:Restaurant 73.28 USD 2011-04-03 * "Starbucks" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -6.41 USD Expenses:Food:Coffee 6.41 USD 2011-04-04 * "Mission Chinese Food" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -20.85 USD Expenses:Food:Restaurant 20.85 USD 2011-04-04 * "Starbucks" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -6.52 USD Expenses:Food:Coffee 6.52 USD 2011-04-05 * "Waterbar" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -42.71 USD Expenses:Food:Restaurant 42.71 USD 2011-04-05 * "Mission Chinese Food" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -29.52 USD Expenses:Food:Restaurant 29.52 USD 2011-04-06 * "Bar Crudo" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -78.01 USD Expenses:Food:Restaurant 78.01 USD 2011-04-06 * "Mission Chinese Food" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -34.71 USD Expenses:Food:Restaurant 34.71 USD 2011-04-07 * "Waterbar" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -37.05 USD Expenses:Food:Restaurant 37.05 USD 2011-04-08 * "Bar Crudo" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -72.04 USD Expenses:Food:Restaurant 72.04 USD 2011-04-08 * "Waterbar" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -59.18 USD Expenses:Food:Restaurant 59.18 USD 2011-04-08 * "Mission Chinese Food" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -23.82 USD Expenses:Food:Restaurant 23.82 USD 2011-04-09 * "Waterbar" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -43.78 USD Expenses:Food:Restaurant 43.78 USD 2011-04-09 * "Starbucks" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -5.97 USD Expenses:Food:Coffee 5.97 USD 2011-04-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 475.57 USD Assets:US:BofA:Checking -475.57 USD 2011-04-10 * "Pizza Delfina" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -16.91 USD Expenses:Food:Restaurant 16.91 USD 2011-04-10 * "Waterbar" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -52.48 USD Expenses:Food:Restaurant 52.48 USD 2011-04-11 * "Bar Crudo" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -78.87 USD Expenses:Food:Restaurant 78.87 USD 2011-04-11 * "Pizza Delfina" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -17.74 USD Expenses:Food:Restaurant 17.74 USD 2011-04-11 * "Waterbar" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -58.04 USD Expenses:Food:Restaurant 58.04 USD 2011-04-11 * "Mission Chinese Food" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -26.17 USD Expenses:Food:Restaurant 26.17 USD 2011-04-12 * "Mission Chinese Food" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -27.60 USD Expenses:Food:Restaurant 27.60 USD 2011-04-12 * "Starbucks" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -6.01 USD Expenses:Food:Coffee 6.01 USD 2011-04-13 * "Bar Crudo" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -71.86 USD Expenses:Food:Restaurant 71.86 USD 2011-04-13 * "Waterbar" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -51.88 USD Expenses:Food:Restaurant 51.88 USD 2011-04-13 * "Starbucks" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -5.44 USD Expenses:Food:Coffee 5.44 USD 2011-04-14 * "Starbucks" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -6.29 USD Expenses:Food:Coffee 6.29 USD 2011-04-16 * "Waterbar" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -40.40 USD Expenses:Food:Restaurant 40.40 USD 2011-04-16 * "Starbucks" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -5.10 USD Expenses:Food:Coffee 5.10 USD 2011-04-17 * "Bar Crudo" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -68.54 USD Expenses:Food:Restaurant 68.54 USD 2011-04-17 * "Waterbar" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -59.29 USD Expenses:Food:Restaurant 59.29 USD 2011-04-17 * "Starbucks" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -5.31 USD Expenses:Food:Coffee 5.31 USD 2011-04-18 * "Bar Crudo" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -72.82 USD Expenses:Food:Restaurant 72.82 USD 2011-04-19 * "Bar Crudo" "" #trip-san-francisco-2011 Liabilities:US:Chase:Slate -79.35 USD Expenses:Food:Restaurant 79.35 USD 2011-04-19 * "Consume vacation days" Assets:US:Hooli:Vacation -136 VACHR Expenses:Vacation 136 VACHR 2011-04-19 event "location" "New Metropolis" 2011-04-21 * "Cafe Modagor" "Eating out with Julie" Liabilities:US:Chase:Slate -33.62 USD Expenses:Food:Restaurant 33.62 USD 2011-04-23 balance Liabilities:US:Chase:Slate -3411.40 USD 2011-04-23 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2011-04-25 * "Rose Flower" "Eating out after work" Liabilities:US:Chase:Slate -32.80 USD Expenses:Food:Restaurant 32.80 USD 2011-04-30 * "China Garden" "Eating out " Liabilities:US:Chase:Slate -32.76 USD Expenses:Food:Restaurant 32.76 USD 2011-05-01 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -33.77 USD Expenses:Food:Restaurant 33.77 USD 2011-05-01 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -90.06 USD Expenses:Food:Groceries 90.06 USD 2011-05-03 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -29.72 USD Expenses:Food:Restaurant 29.72 USD 2011-05-06 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -40.59 USD Expenses:Food:Restaurant 40.59 USD 2011-05-08 * "Rose Flower" "Eating out after work" Liabilities:US:Chase:Slate -23.98 USD Expenses:Food:Restaurant 23.98 USD 2011-05-08 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 403.68 USD Assets:US:BofA:Checking -403.68 USD 2011-05-11 * "Goba Goba" "Eating out " Liabilities:US:Chase:Slate -21.02 USD Expenses:Food:Restaurant 21.02 USD 2011-05-12 * "Kin Soy" "Eating out with Bill" Liabilities:US:Chase:Slate -31.72 USD Expenses:Food:Restaurant 31.72 USD 2011-05-12 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -99.31 USD Expenses:Food:Groceries 99.31 USD 2011-05-17 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -29.10 USD Expenses:Food:Restaurant 29.10 USD 2011-05-19 balance Liabilities:US:Chase:Slate -3592.55 USD 2011-05-20 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -50.76 USD Expenses:Food:Restaurant 50.76 USD 2011-05-21 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -23.83 USD Expenses:Food:Restaurant 23.83 USD 2011-05-25 * "Kin Soy" "Eating out with work buddies" Liabilities:US:Chase:Slate -23.11 USD Expenses:Food:Restaurant 23.11 USD 2011-05-25 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2011-05-27 * "Rose Flower" "Eating out " Liabilities:US:Chase:Slate -25.44 USD Expenses:Food:Restaurant 25.44 USD 2011-05-30 * "Uncle Boons" "Eating out with Julie" Liabilities:US:Chase:Slate -45.23 USD Expenses:Food:Restaurant 45.23 USD 2011-05-30 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -84.39 USD Expenses:Food:Groceries 84.39 USD 2011-06-01 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -28.86 USD Expenses:Food:Restaurant 28.86 USD 2011-06-06 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -44.88 USD Expenses:Food:Restaurant 44.88 USD 2011-06-06 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -144.36 USD Expenses:Food:Groceries 144.36 USD 2011-06-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 795.07 USD Assets:US:BofA:Checking -795.07 USD 2011-06-09 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -23.06 USD Expenses:Food:Restaurant 23.06 USD 2011-06-14 balance Liabilities:US:Chase:Slate -3411.40 USD 2011-06-14 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -35.33 USD Expenses:Food:Restaurant 35.33 USD 2011-06-14 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -124.96 USD Expenses:Food:Groceries 124.96 USD 2011-06-19 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -40.32 USD Expenses:Food:Restaurant 40.32 USD 2011-06-21 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2011-06-22 * "Goba Goba" "Eating out " Liabilities:US:Chase:Slate -22.32 USD Expenses:Food:Restaurant 22.32 USD 2011-06-26 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -16.04 USD Expenses:Food:Restaurant 16.04 USD 2011-06-26 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -65.30 USD Expenses:Food:Groceries 65.30 USD 2011-06-28 * "Uncle Boons" "Eating out " Liabilities:US:Chase:Slate -50.86 USD Expenses:Food:Restaurant 50.86 USD 2011-07-02 * "Rose Flower" "Eating out with work buddies" Liabilities:US:Chase:Slate -32.48 USD Expenses:Food:Restaurant 32.48 USD 2011-07-04 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -112.39 USD Expenses:Food:Groceries 112.39 USD 2011-07-05 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -43.19 USD Expenses:Food:Restaurant 43.19 USD 2011-07-06 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -27.18 USD Expenses:Food:Restaurant 27.18 USD 2011-07-09 balance Liabilities:US:Chase:Slate -4101.77 USD 2011-07-09 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -40.66 USD Expenses:Food:Restaurant 40.66 USD 2011-07-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 785.50 USD Assets:US:BofA:Checking -785.50 USD 2011-07-12 * "Uncle Boons" "Eating out after work" Liabilities:US:Chase:Slate -54.47 USD Expenses:Food:Restaurant 54.47 USD 2011-07-14 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -22.84 USD Expenses:Food:Restaurant 22.84 USD 2011-07-17 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -30.16 USD Expenses:Food:Restaurant 30.16 USD 2011-07-21 * "China Garden" "Eating out with Julie" Liabilities:US:Chase:Slate -38.80 USD Expenses:Food:Restaurant 38.80 USD 2011-07-23 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2011-07-24 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -106.57 USD Expenses:Food:Groceries 106.57 USD 2011-07-25 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -19.89 USD Expenses:Food:Restaurant 19.89 USD 2011-07-30 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -19.62 USD Expenses:Food:Restaurant 19.62 USD 2011-08-04 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -26.05 USD Expenses:Food:Restaurant 26.05 USD 2011-08-05 balance Liabilities:US:Chase:Slate -3795.33 USD 2011-08-07 * "China Garden" "Eating out with work buddies" Liabilities:US:Chase:Slate -19.69 USD Expenses:Food:Restaurant 19.69 USD 2011-08-10 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -23.84 USD Expenses:Food:Restaurant 23.84 USD 2011-08-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 457.31 USD Assets:US:BofA:Checking -457.31 USD 2011-08-13 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -29.85 USD Expenses:Food:Restaurant 29.85 USD 2011-08-13 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -102.13 USD Expenses:Food:Groceries 102.13 USD 2011-08-14 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -41.39 USD Expenses:Food:Restaurant 41.39 USD 2011-08-18 * "China Garden" "Eating out with Bill" Liabilities:US:Chase:Slate -27.94 USD Expenses:Food:Restaurant 27.94 USD 2011-08-19 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -79.17 USD Expenses:Food:Restaurant 79.17 USD 2011-08-22 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2011-08-23 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -28.97 USD Expenses:Food:Restaurant 28.97 USD 2011-08-24 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -39.31 USD Expenses:Food:Restaurant 39.31 USD 2011-08-24 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -101.56 USD Expenses:Food:Groceries 101.56 USD 2011-08-29 * "Cafe Modagor" "Eating out with Bill" Liabilities:US:Chase:Slate -40.41 USD Expenses:Food:Restaurant 40.41 USD 2011-09-01 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -30.47 USD Expenses:Food:Restaurant 30.47 USD 2011-09-02 balance Liabilities:US:Chase:Slate -4022.75 USD 2011-09-04 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -43.65 USD Expenses:Food:Restaurant 43.65 USD 2011-09-06 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -105.52 USD Expenses:Food:Groceries 105.52 USD 2011-09-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 783.22 USD Assets:US:BofA:Checking -783.22 USD 2011-09-09 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -22.70 USD Expenses:Food:Restaurant 22.70 USD 2011-09-12 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -43.74 USD Expenses:Food:Restaurant 43.74 USD 2011-09-13 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -110.61 USD Expenses:Food:Groceries 110.61 USD 2011-09-17 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -27.10 USD Expenses:Food:Restaurant 27.10 USD 2011-09-20 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2011-09-21 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -23.73 USD Expenses:Food:Restaurant 23.73 USD 2011-09-26 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -17.87 USD Expenses:Food:Restaurant 17.87 USD 2011-09-27 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -16.67 USD Expenses:Food:Restaurant 16.67 USD 2011-09-27 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -99.27 USD Expenses:Food:Groceries 99.27 USD 2011-09-29 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -42.96 USD Expenses:Food:Restaurant 42.96 USD 2011-09-30 balance Liabilities:US:Chase:Slate -3913.35 USD 2011-10-01 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -32.24 USD Expenses:Food:Restaurant 32.24 USD 2011-10-05 * "China Garden" "Eating out with Natasha" Liabilities:US:Chase:Slate -50.33 USD Expenses:Food:Restaurant 50.33 USD 2011-10-05 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -86.19 USD Expenses:Food:Groceries 86.19 USD 2011-10-06 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -24.67 USD Expenses:Food:Restaurant 24.67 USD 2011-10-08 * "China Garden" "Eating out " Liabilities:US:Chase:Slate -48.46 USD Expenses:Food:Restaurant 48.46 USD 2011-10-11 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -123.90 USD Expenses:Food:Groceries 123.90 USD 2011-10-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 867.74 USD Assets:US:BofA:Checking -867.74 USD 2011-10-12 * "China Garden" "Eating out with work buddies" Liabilities:US:Chase:Slate -14.48 USD Expenses:Food:Restaurant 14.48 USD 2011-10-13 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -40.21 USD Expenses:Food:Restaurant 40.21 USD 2011-10-18 * "Cafe Modagor" "Eating out with Bill" Liabilities:US:Chase:Slate -40.34 USD Expenses:Food:Restaurant 40.34 USD 2011-10-18 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2011-10-20 * "Kin Soy" "Eating out after work" Liabilities:US:Chase:Slate -27.64 USD Expenses:Food:Restaurant 27.64 USD 2011-10-22 balance Liabilities:US:Chase:Slate -3654.07 USD 2011-10-24 * "Chichipotle" "Eating out after work" Liabilities:US:Chase:Slate -30.55 USD Expenses:Food:Restaurant 30.55 USD 2011-10-28 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -38.31 USD Expenses:Food:Restaurant 38.31 USD 2011-10-30 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -83.14 USD Expenses:Food:Groceries 83.14 USD 2011-11-01 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -48.09 USD Expenses:Food:Restaurant 48.09 USD 2011-11-06 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -25.51 USD Expenses:Food:Restaurant 25.51 USD 2011-11-08 * "Uncle Boons" "Eating out with Bill" Liabilities:US:Chase:Slate -27.04 USD Expenses:Food:Restaurant 27.04 USD 2011-11-10 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -82.86 USD Expenses:Food:Groceries 82.86 USD 2011-11-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 596.55 USD Assets:US:BofA:Checking -596.55 USD 2011-11-13 * "China Garden" "Eating out with Bill" Liabilities:US:Chase:Slate -18.38 USD Expenses:Food:Restaurant 18.38 USD 2011-11-14 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2011-11-16 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -93.84 USD Expenses:Food:Groceries 93.84 USD 2011-11-17 balance Liabilities:US:Chase:Slate -3625.24 USD 2011-11-17 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -20.52 USD Expenses:Food:Restaurant 20.52 USD 2011-11-20 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -44.30 USD Expenses:Food:Restaurant 44.30 USD 2011-11-25 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -23.07 USD Expenses:Food:Restaurant 23.07 USD 2011-11-28 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -20.88 USD Expenses:Food:Restaurant 20.88 USD 2011-11-29 * "China Garden" "Eating out with Julie" Liabilities:US:Chase:Slate -38.57 USD Expenses:Food:Restaurant 38.57 USD 2011-12-04 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -25.17 USD Expenses:Food:Restaurant 25.17 USD 2011-12-06 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -37.25 USD Expenses:Food:Restaurant 37.25 USD 2011-12-06 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -99.01 USD Expenses:Food:Groceries 99.01 USD 2011-12-07 * "Chichipotle" "Eating out with Natasha" Liabilities:US:Chase:Slate -31.09 USD Expenses:Food:Restaurant 31.09 USD 2011-12-08 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 593.93 USD Assets:US:BofA:Checking -593.93 USD 2011-12-09 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -40.23 USD Expenses:Food:Restaurant 40.23 USD 2011-12-10 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -15.99 USD Expenses:Food:Restaurant 15.99 USD 2011-12-12 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2011-12-14 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -20.28 USD Expenses:Food:Restaurant 20.28 USD 2011-12-15 balance Liabilities:US:Chase:Slate -3567.67 USD 2011-12-18 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -45.43 USD Expenses:Food:Groceries 45.43 USD 2011-12-19 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -31.52 USD Expenses:Food:Restaurant 31.52 USD 2011-12-20 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -28.14 USD Expenses:Food:Restaurant 28.14 USD 2011-12-25 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -50.94 USD Expenses:Food:Restaurant 50.94 USD 2011-12-30 * "Chichipotle" "Eating out after work" Liabilities:US:Chase:Slate -46.39 USD Expenses:Food:Restaurant 46.39 USD 2012-01-03 * "China Garden" "Eating out " Liabilities:US:Chase:Slate -23.71 USD Expenses:Food:Restaurant 23.71 USD 2012-01-04 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -80.14 USD Expenses:Food:Groceries 80.14 USD 2012-01-06 balance Liabilities:US:Chase:Slate -3873.94 USD 2012-01-06 * "China Garden" "Eating out with work buddies" Liabilities:US:Chase:Slate -65.60 USD Expenses:Food:Restaurant 65.60 USD 2012-01-08 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -27.15 USD Expenses:Food:Restaurant 27.15 USD 2012-01-08 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2012-01-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 707.87 USD Assets:US:BofA:Checking -707.87 USD 2012-01-12 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -32.58 USD Expenses:Food:Restaurant 32.58 USD 2012-01-14 * "Uncle Boons" "Eating out with Bill" Liabilities:US:Chase:Slate -29.53 USD Expenses:Food:Restaurant 29.53 USD 2012-01-14 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -69.05 USD Expenses:Food:Groceries 69.05 USD 2012-01-17 * "Chichipotle" "Eating out with Natasha" Liabilities:US:Chase:Slate -16.62 USD Expenses:Food:Restaurant 16.62 USD 2012-01-18 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -24.88 USD Expenses:Food:Restaurant 24.88 USD 2012-01-19 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -12.90 USD Expenses:Food:Restaurant 12.90 USD 2012-01-23 * "Chichipotle" "Eating out with work buddies" Liabilities:US:Chase:Slate -40.54 USD Expenses:Food:Restaurant 40.54 USD 2012-01-27 balance Liabilities:US:Chase:Slate -3604.92 USD 2012-01-28 * "China Garden" "Eating out with Julie" Liabilities:US:Chase:Slate -37.00 USD Expenses:Food:Restaurant 37.00 USD 2012-01-30 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -51.25 USD Expenses:Food:Groceries 51.25 USD 2012-01-31 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -17.81 USD Expenses:Food:Restaurant 17.81 USD 2012-02-03 * "Jewel of Morroco" "Eating out with Natasha" Liabilities:US:Chase:Slate -21.00 USD Expenses:Food:Restaurant 21.00 USD 2012-02-06 * "Goba Goba" "Eating out " Liabilities:US:Chase:Slate -30.66 USD Expenses:Food:Restaurant 30.66 USD 2012-02-07 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -39.45 USD Expenses:Food:Restaurant 39.45 USD 2012-02-07 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2012-02-10 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -67.26 USD Expenses:Food:Restaurant 67.26 USD 2012-02-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 577.95 USD Assets:US:BofA:Checking -577.95 USD 2012-02-12 * "Kin Soy" "Eating out with Julie" Liabilities:US:Chase:Slate -28.20 USD Expenses:Food:Restaurant 28.20 USD 2012-02-15 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -16.84 USD Expenses:Food:Restaurant 16.84 USD 2012-02-18 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -77.30 USD Expenses:Food:Groceries 77.30 USD 2012-02-20 * "Kin Soy" "Eating out with Julie" Liabilities:US:Chase:Slate -19.41 USD Expenses:Food:Restaurant 19.41 USD 2012-02-23 balance Liabilities:US:Chase:Slate -3553.15 USD 2012-02-24 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -23.02 USD Expenses:Food:Restaurant 23.02 USD 2012-02-26 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -29.01 USD Expenses:Food:Restaurant 29.01 USD 2012-03-01 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -24.92 USD Expenses:Food:Restaurant 24.92 USD 2012-03-03 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -95.06 USD Expenses:Food:Groceries 95.06 USD 2012-03-05 * "Kin Soy" "Eating out with Bill" Liabilities:US:Chase:Slate -11.28 USD Expenses:Food:Restaurant 11.28 USD 2012-03-05 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2012-03-07 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -43.75 USD Expenses:Food:Restaurant 43.75 USD 2012-03-08 * "China Garden" "Eating out " Liabilities:US:Chase:Slate -26.12 USD Expenses:Food:Restaurant 26.12 USD 2012-03-10 * "Jewel of Morroco" "Eating out with work buddies" Liabilities:US:Chase:Slate -38.50 USD Expenses:Food:Restaurant 38.50 USD 2012-03-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 553.41 USD Assets:US:BofA:Checking -553.41 USD 2012-03-13 * "Uncle Boons" "Eating out " Liabilities:US:Chase:Slate -25.60 USD Expenses:Food:Restaurant 25.60 USD 2012-03-15 * "Jewel of Morroco" "Eating out with Julie" Liabilities:US:Chase:Slate -31.41 USD Expenses:Food:Restaurant 31.41 USD 2012-03-16 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -33.55 USD Expenses:Food:Restaurant 33.55 USD 2012-03-16 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -101.94 USD Expenses:Food:Groceries 101.94 USD 2012-03-20 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -34.10 USD Expenses:Food:Restaurant 34.10 USD 2012-03-21 * "China Garden" "Eating out with work buddies" Liabilities:US:Chase:Slate -40.84 USD Expenses:Food:Restaurant 40.84 USD 2012-03-23 balance Liabilities:US:Chase:Slate -3678.84 USD 2012-03-24 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -14.30 USD Expenses:Food:Restaurant 14.30 USD 2012-03-24 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -84.37 USD Expenses:Food:Groceries 84.37 USD 2012-03-28 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -25.50 USD Expenses:Food:Restaurant 25.50 USD 2012-03-30 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -34.87 USD Expenses:Food:Restaurant 34.87 USD 2012-04-01 * "Uncle Boons" "Eating out with Julie" Liabilities:US:Chase:Slate -26.12 USD Expenses:Food:Restaurant 26.12 USD 2012-04-03 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -24.62 USD Expenses:Food:Restaurant 24.62 USD 2012-04-04 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -28.72 USD Expenses:Food:Restaurant 28.72 USD 2012-04-05 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2012-04-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 654.87 USD Assets:US:BofA:Checking -654.87 USD 2012-04-08 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -28.93 USD Expenses:Food:Restaurant 28.93 USD 2012-04-08 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -70.15 USD Expenses:Food:Groceries 70.15 USD 2012-04-09 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -36.03 USD Expenses:Food:Restaurant 36.03 USD 2012-04-11 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -51.74 USD Expenses:Food:Restaurant 51.74 USD 2012-04-16 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -31.82 USD Expenses:Food:Restaurant 31.82 USD 2012-04-18 balance Liabilities:US:Chase:Slate -3601.14 USD 2012-04-18 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -65.33 USD Expenses:Food:Groceries 65.33 USD 2012-04-21 event "location" "Boston" 2012-04-23 * "Franklin Cafe" "" #trip-boston-2012 Liabilities:US:Chase:Slate -35.07 USD Expenses:Food:Restaurant 35.07 USD 2012-04-23 * "Starbucks" "" #trip-boston-2012 Liabilities:US:Chase:Slate -7.20 USD Expenses:Food:Coffee 7.20 USD 2012-04-27 * "Franklin Cafe" "" #trip-boston-2012 Liabilities:US:Chase:Slate -25.36 USD Expenses:Food:Restaurant 25.36 USD 2012-04-29 * "Legal Seafood" "" #trip-boston-2012 Liabilities:US:Chase:Slate -45.75 USD Expenses:Food:Restaurant 45.75 USD 2012-04-30 * "Giacomo's Restaurant" "" #trip-boston-2012 Liabilities:US:Chase:Slate -46.31 USD Expenses:Food:Restaurant 46.31 USD 2012-04-30 * "Legal Seafood" "" #trip-boston-2012 Liabilities:US:Chase:Slate -36.22 USD Expenses:Food:Restaurant 36.22 USD 2012-04-30 * "Starbucks" "" #trip-boston-2012 Liabilities:US:Chase:Slate -5.65 USD Expenses:Food:Coffee 5.65 USD 2012-05-01 * "Legal Seafood" "" #trip-boston-2012 Liabilities:US:Chase:Slate -42.96 USD Expenses:Food:Restaurant 42.96 USD 2012-05-02 * "Giacomo's Restaurant" "" #trip-boston-2012 Liabilities:US:Chase:Slate -39.44 USD Expenses:Food:Restaurant 39.44 USD 2012-05-02 * "Starbucks" "" #trip-boston-2012 Liabilities:US:Chase:Slate -5.73 USD Expenses:Food:Coffee 5.73 USD 2012-05-03 * "Giacomo's Restaurant" "" #trip-boston-2012 Liabilities:US:Chase:Slate -49.25 USD Expenses:Food:Restaurant 49.25 USD 2012-05-03 * "Franklin Cafe" "" #trip-boston-2012 Liabilities:US:Chase:Slate -32.32 USD Expenses:Food:Restaurant 32.32 USD 2012-05-04 * "Franklin Cafe" "" #trip-boston-2012 Liabilities:US:Chase:Slate -27.77 USD Expenses:Food:Restaurant 27.77 USD 2012-05-07 * "Giacomo's Restaurant" "" #trip-boston-2012 Liabilities:US:Chase:Slate -37.14 USD Expenses:Food:Restaurant 37.14 USD 2012-05-07 * "Franklin Cafe" "" #trip-boston-2012 Liabilities:US:Chase:Slate -31.94 USD Expenses:Food:Restaurant 31.94 USD 2012-05-08 * "Starbucks" "" #trip-boston-2012 Liabilities:US:Chase:Slate -5.74 USD Expenses:Food:Coffee 5.74 USD 2012-05-09 * "Franklin Cafe" "" #trip-boston-2012 Liabilities:US:Chase:Slate -35.53 USD Expenses:Food:Restaurant 35.53 USD 2012-05-10 * "Legal Seafood" "" #trip-boston-2012 Liabilities:US:Chase:Slate -39.63 USD Expenses:Food:Restaurant 39.63 USD 2012-05-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 294.43 USD Assets:US:BofA:Checking -294.43 USD 2012-05-11 balance Liabilities:US:Chase:Slate -3921.05 USD 2012-05-11 * "Giacomo's Restaurant" "" #trip-boston-2012 Liabilities:US:Chase:Slate -35.86 USD Expenses:Food:Restaurant 35.86 USD 2012-05-11 * "Franklin Cafe" "" #trip-boston-2012 Liabilities:US:Chase:Slate -35.16 USD Expenses:Food:Restaurant 35.16 USD 2012-05-13 * "Kin Soy" "Eating out after work" Liabilities:US:Chase:Slate -39.36 USD Expenses:Food:Restaurant 39.36 USD 2012-05-13 * "Consume vacation days" Assets:US:Hooli:Vacation -176 VACHR Expenses:Vacation 176 VACHR 2012-05-13 event "location" "New Metropolis" 2012-05-14 * "Jewel of Morroco" "Eating out with Natasha" Liabilities:US:Chase:Slate -15.31 USD Expenses:Food:Restaurant 15.31 USD 2012-05-18 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -47.11 USD Expenses:Food:Restaurant 47.11 USD 2012-05-21 * "Rose Flower" "Eating out " Liabilities:US:Chase:Slate -47.50 USD Expenses:Food:Restaurant 47.50 USD 2012-05-23 * "Jewel of Morroco" "Eating out with Julie" Liabilities:US:Chase:Slate -21.25 USD Expenses:Food:Restaurant 21.25 USD 2012-05-23 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -71.42 USD Expenses:Food:Groceries 71.42 USD 2012-05-27 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -34.75 USD Expenses:Food:Restaurant 34.75 USD 2012-05-29 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -66.39 USD Expenses:Food:Restaurant 66.39 USD 2012-06-01 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2012-06-03 * "Chichipotle" "Eating out with work buddies" Liabilities:US:Chase:Slate -16.15 USD Expenses:Food:Restaurant 16.15 USD 2012-06-04 balance Liabilities:US:Chase:Slate -4471.31 USD 2012-06-07 * "Uncle Boons" "Eating out " Liabilities:US:Chase:Slate -75.32 USD Expenses:Food:Restaurant 75.32 USD 2012-06-09 * "Rose Flower" "Eating out with work buddies" Liabilities:US:Chase:Slate -51.82 USD Expenses:Food:Restaurant 51.82 USD 2012-06-11 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -73.55 USD Expenses:Food:Groceries 73.55 USD 2012-06-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 640.57 USD Assets:US:BofA:Checking -640.57 USD 2012-06-12 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -31.40 USD Expenses:Food:Restaurant 31.40 USD 2012-06-14 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -30.18 USD Expenses:Food:Restaurant 30.18 USD 2012-06-17 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -14.98 USD Expenses:Food:Restaurant 14.98 USD 2012-06-20 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -15.57 USD Expenses:Food:Restaurant 15.57 USD 2012-06-24 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -27.11 USD Expenses:Food:Restaurant 27.11 USD 2012-06-24 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -51.29 USD Expenses:Food:Groceries 51.29 USD 2012-06-27 * "Kin Soy" "Eating out with Bill" Liabilities:US:Chase:Slate -38.68 USD Expenses:Food:Restaurant 38.68 USD 2012-06-29 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2012-06-30 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -89.47 USD Expenses:Food:Groceries 89.47 USD 2012-07-02 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -38.26 USD Expenses:Food:Restaurant 38.26 USD 2012-07-03 balance Liabilities:US:Chase:Slate -4488.37 USD 2012-07-07 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -27.31 USD Expenses:Food:Restaurant 27.31 USD 2012-07-07 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -63.01 USD Expenses:Food:Groceries 63.01 USD 2012-07-09 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -41.69 USD Expenses:Food:Restaurant 41.69 USD 2012-07-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 648.00 USD Assets:US:BofA:Checking -648.00 USD 2012-07-13 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -59.05 USD Expenses:Food:Restaurant 59.05 USD 2012-07-17 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -30.03 USD Expenses:Food:Restaurant 30.03 USD 2012-07-20 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -33.18 USD Expenses:Food:Restaurant 33.18 USD 2012-07-21 * "Kin Soy" "Eating out with Julie" Liabilities:US:Chase:Slate -32.01 USD Expenses:Food:Restaurant 32.01 USD 2012-07-25 balance Liabilities:US:Chase:Slate -4126.65 USD 2012-07-25 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -81.66 USD Expenses:Food:Groceries 81.66 USD 2012-07-26 * "Kin Soy" "Eating out with Bill" Liabilities:US:Chase:Slate -52.32 USD Expenses:Food:Restaurant 52.32 USD 2012-07-27 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2012-07-31 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -31.45 USD Expenses:Food:Restaurant 31.45 USD 2012-08-02 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -96.81 USD Expenses:Food:Groceries 96.81 USD 2012-08-05 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -17.96 USD Expenses:Food:Restaurant 17.96 USD 2012-08-06 * "China Garden" "Eating out with Natasha" Liabilities:US:Chase:Slate -30.61 USD Expenses:Food:Restaurant 30.61 USD 2012-08-08 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 557.83 USD Assets:US:BofA:Checking -557.83 USD 2012-08-09 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -31.80 USD Expenses:Food:Restaurant 31.80 USD 2012-08-09 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -85.17 USD Expenses:Food:Groceries 85.17 USD 2012-08-14 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -29.90 USD Expenses:Food:Restaurant 29.90 USD 2012-08-17 balance Liabilities:US:Chase:Slate -4146.50 USD 2012-08-17 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -24.99 USD Expenses:Food:Restaurant 24.99 USD 2012-08-18 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -47.23 USD Expenses:Food:Restaurant 47.23 USD 2012-08-20 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -63.23 USD Expenses:Food:Groceries 63.23 USD 2012-08-22 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -28.15 USD Expenses:Food:Restaurant 28.15 USD 2012-08-24 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -33.31 USD Expenses:Food:Restaurant 33.31 USD 2012-08-26 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -31.61 USD Expenses:Food:Restaurant 31.61 USD 2012-08-28 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2012-08-29 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -25.77 USD Expenses:Food:Restaurant 25.77 USD 2012-08-30 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -75.05 USD Expenses:Food:Groceries 75.05 USD 2012-09-02 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -28.46 USD Expenses:Food:Restaurant 28.46 USD 2012-09-05 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -26.79 USD Expenses:Food:Restaurant 26.79 USD 2012-09-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 644.52 USD Assets:US:BofA:Checking -644.52 USD 2012-09-08 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -24.86 USD Expenses:Food:Restaurant 24.86 USD 2012-09-09 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -29.97 USD Expenses:Food:Restaurant 29.97 USD 2012-09-10 balance Liabilities:US:Chase:Slate -4061.40 USD 2012-09-12 * "Kin Soy" "Eating out with Julie" Liabilities:US:Chase:Slate -31.45 USD Expenses:Food:Restaurant 31.45 USD 2012-09-12 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -81.26 USD Expenses:Food:Groceries 81.26 USD 2012-09-13 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -57.09 USD Expenses:Food:Restaurant 57.09 USD 2012-09-16 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -24.20 USD Expenses:Food:Restaurant 24.20 USD 2012-09-19 * "Uncle Boons" "Eating out with Bill" Liabilities:US:Chase:Slate -34.88 USD Expenses:Food:Restaurant 34.88 USD 2012-09-20 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -65.72 USD Expenses:Food:Groceries 65.72 USD 2012-09-24 * "China Garden" "Eating out with Natasha" Liabilities:US:Chase:Slate -35.05 USD Expenses:Food:Restaurant 35.05 USD 2012-09-26 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2012-09-27 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -31.93 USD Expenses:Food:Restaurant 31.93 USD 2012-09-28 * "Cafe Modagor" "Eating out with Julie" Liabilities:US:Chase:Slate -25.14 USD Expenses:Food:Restaurant 25.14 USD 2012-09-29 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -55.97 USD Expenses:Food:Restaurant 55.97 USD 2012-10-01 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -55.84 USD Expenses:Food:Restaurant 55.84 USD 2012-10-02 balance Liabilities:US:Chase:Slate -4679.93 USD 2012-10-03 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -41.79 USD Expenses:Food:Restaurant 41.79 USD 2012-10-04 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -16.53 USD Expenses:Food:Restaurant 16.53 USD 2012-10-09 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -23.21 USD Expenses:Food:Restaurant 23.21 USD 2012-10-10 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -67.73 USD Expenses:Food:Groceries 67.73 USD 2012-10-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 824.84 USD Assets:US:BofA:Checking -824.84 USD 2012-10-14 * "Chichipotle" "Eating out with Natasha" Liabilities:US:Chase:Slate -27.08 USD Expenses:Food:Restaurant 27.08 USD 2012-10-19 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -55.96 USD Expenses:Food:Restaurant 55.96 USD 2012-10-22 * "Uncle Boons" "Eating out after work" Liabilities:US:Chase:Slate -29.26 USD Expenses:Food:Restaurant 29.26 USD 2012-10-23 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -33.62 USD Expenses:Food:Restaurant 33.62 USD 2012-10-26 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -65.24 USD Expenses:Food:Groceries 65.24 USD 2012-10-27 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2012-10-28 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -31.90 USD Expenses:Food:Restaurant 31.90 USD 2012-11-01 balance Liabilities:US:Chase:Slate -4367.41 USD 2012-11-01 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -25.28 USD Expenses:Food:Restaurant 25.28 USD 2012-11-03 * "Goba Goba" "Eating out " Liabilities:US:Chase:Slate -16.82 USD Expenses:Food:Restaurant 16.82 USD 2012-11-05 * "Rose Flower" "Eating out with work buddies" Liabilities:US:Chase:Slate -29.63 USD Expenses:Food:Restaurant 29.63 USD 2012-11-10 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -27.39 USD Expenses:Food:Restaurant 27.39 USD 2012-11-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 460.89 USD Assets:US:BofA:Checking -460.89 USD 2012-11-13 * "Kin Soy" "Eating out after work" Liabilities:US:Chase:Slate -25.79 USD Expenses:Food:Restaurant 25.79 USD 2012-11-15 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -75.57 USD Expenses:Food:Groceries 75.57 USD 2012-11-18 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -30.54 USD Expenses:Food:Restaurant 30.54 USD 2012-11-20 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -26.44 USD Expenses:Food:Restaurant 26.44 USD 2012-11-21 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -81.63 USD Expenses:Food:Groceries 81.63 USD 2012-11-22 balance Liabilities:US:Chase:Slate -4245.61 USD 2012-11-25 * "Cafe Modagor" "Eating out with Julie" Liabilities:US:Chase:Slate -39.48 USD Expenses:Food:Restaurant 39.48 USD 2012-11-27 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -72.47 USD Expenses:Food:Groceries 72.47 USD 2012-11-27 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2012-11-30 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -41.15 USD Expenses:Food:Restaurant 41.15 USD 2012-12-01 * "Jewel of Morroco" "Eating out with work buddies" Liabilities:US:Chase:Slate -33.50 USD Expenses:Food:Restaurant 33.50 USD 2012-12-06 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -10.02 USD Expenses:Food:Restaurant 10.02 USD 2012-12-06 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -65.14 USD Expenses:Food:Groceries 65.14 USD 2012-12-09 * "Uncle Boons" "Eating out with Bill" Liabilities:US:Chase:Slate -27.98 USD Expenses:Food:Restaurant 27.98 USD 2012-12-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 623.92 USD Assets:US:BofA:Checking -623.92 USD 2012-12-11 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -35.91 USD Expenses:Food:Restaurant 35.91 USD 2012-12-13 * "Uncle Boons" "Eating out " Liabilities:US:Chase:Slate -35.52 USD Expenses:Food:Restaurant 35.52 USD 2012-12-17 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -18.07 USD Expenses:Food:Restaurant 18.07 USD 2012-12-18 balance Liabilities:US:Chase:Slate -4120.93 USD 2012-12-21 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -32.69 USD Expenses:Food:Restaurant 32.69 USD 2012-12-24 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -34.23 USD Expenses:Food:Restaurant 34.23 USD 2012-12-26 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -26.78 USD Expenses:Food:Restaurant 26.78 USD 2012-12-26 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -61.86 USD Expenses:Food:Groceries 61.86 USD 2012-12-27 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -22.16 USD Expenses:Food:Restaurant 22.16 USD 2012-12-30 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2012-12-31 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -47.74 USD Expenses:Food:Restaurant 47.74 USD 2013-01-03 * "Kin Soy" "Eating out with Bill" Liabilities:US:Chase:Slate -12.52 USD Expenses:Food:Restaurant 12.52 USD 2013-01-03 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -93.36 USD Expenses:Food:Groceries 93.36 USD 2013-01-07 balance Liabilities:US:Chase:Slate -4572.27 USD 2013-01-07 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -30.63 USD Expenses:Food:Restaurant 30.63 USD 2013-01-08 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -75.27 USD Expenses:Food:Groceries 75.27 USD 2013-01-10 * "Goba Goba" "Eating out " Liabilities:US:Chase:Slate -42.47 USD Expenses:Food:Restaurant 42.47 USD 2013-01-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 725.95 USD Assets:US:BofA:Checking -725.95 USD 2013-01-12 * "Jewel of Morroco" "Eating out with Julie" Liabilities:US:Chase:Slate -36.74 USD Expenses:Food:Restaurant 36.74 USD 2013-01-17 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -37.17 USD Expenses:Food:Restaurant 37.17 USD 2013-01-21 * "Cafe Modagor" "Eating out with Bill" Liabilities:US:Chase:Slate -15.39 USD Expenses:Food:Restaurant 15.39 USD 2013-01-22 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -138.82 USD Expenses:Food:Groceries 138.82 USD 2013-01-25 * "Goba Goba" "Eating out alone" Liabilities:US:Chase:Slate -24.15 USD Expenses:Food:Restaurant 24.15 USD 2013-01-26 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2013-01-27 * "Chichipotle" "Eating out with Natasha" Liabilities:US:Chase:Slate -41.03 USD Expenses:Food:Restaurant 41.03 USD 2013-01-30 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -19.86 USD Expenses:Food:Restaurant 19.86 USD 2013-01-31 balance Liabilities:US:Chase:Slate -4427.85 USD 2013-01-31 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -44.46 USD Expenses:Food:Restaurant 44.46 USD 2013-02-02 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -15.65 USD Expenses:Food:Restaurant 15.65 USD 2013-02-03 * "Jewel of Morroco" "Eating out with Natasha" Liabilities:US:Chase:Slate -23.79 USD Expenses:Food:Restaurant 23.79 USD 2013-02-06 * "Chichipotle" "Eating out with Natasha" Liabilities:US:Chase:Slate -29.77 USD Expenses:Food:Restaurant 29.77 USD 2013-02-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 588.35 USD Assets:US:BofA:Checking -588.35 USD 2013-02-09 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -78.26 USD Expenses:Food:Groceries 78.26 USD 2013-02-11 * "Jewel of Morroco" "Eating out with Julie" Liabilities:US:Chase:Slate -42.34 USD Expenses:Food:Restaurant 42.34 USD 2013-02-13 * "Chichipotle" "Eating out after work" Liabilities:US:Chase:Slate -36.50 USD Expenses:Food:Restaurant 36.50 USD 2013-02-14 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -42.10 USD Expenses:Food:Restaurant 42.10 USD 2013-02-15 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -64.48 USD Expenses:Food:Groceries 64.48 USD 2013-02-19 * "Uncle Boons" "Eating out with Bill" Liabilities:US:Chase:Slate -48.20 USD Expenses:Food:Restaurant 48.20 USD 2013-02-23 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -8.51 USD Expenses:Food:Restaurant 8.51 USD 2013-02-26 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2013-02-28 balance Liabilities:US:Chase:Slate -4393.56 USD 2013-02-28 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -35.68 USD Expenses:Food:Restaurant 35.68 USD 2013-03-03 * "Rose Flower" "Eating out " Liabilities:US:Chase:Slate -40.32 USD Expenses:Food:Restaurant 40.32 USD 2013-03-07 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -58.17 USD Expenses:Food:Groceries 58.17 USD 2013-03-08 * "Uncle Boons" "Eating out with Bill" Liabilities:US:Chase:Slate -25.78 USD Expenses:Food:Restaurant 25.78 USD 2013-03-09 * "Kin Soy" "Eating out with Bill" Liabilities:US:Chase:Slate -53.81 USD Expenses:Food:Restaurant 53.81 USD 2013-03-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 575.89 USD Assets:US:BofA:Checking -575.89 USD 2013-03-11 * "Kin Soy" "Eating out with work buddies" Liabilities:US:Chase:Slate -33.83 USD Expenses:Food:Restaurant 33.83 USD 2013-03-13 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -45.80 USD Expenses:Food:Restaurant 45.80 USD 2013-03-14 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -33.45 USD Expenses:Food:Restaurant 33.45 USD 2013-03-16 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -23.53 USD Expenses:Food:Restaurant 23.53 USD 2013-03-21 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -33.93 USD Expenses:Food:Restaurant 33.93 USD 2013-03-22 * "Kin Soy" "Eating out after work" Liabilities:US:Chase:Slate -37.61 USD Expenses:Food:Restaurant 37.61 USD 2013-03-23 balance Liabilities:US:Chase:Slate -4239.58 USD 2013-03-24 * "Uncle Boons" "Eating out after work" Liabilities:US:Chase:Slate -30.70 USD Expenses:Food:Restaurant 30.70 USD 2013-03-24 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -84.83 USD Expenses:Food:Groceries 84.83 USD 2013-03-25 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -26.31 USD Expenses:Food:Restaurant 26.31 USD 2013-03-28 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -29.35 USD Expenses:Food:Restaurant 29.35 USD 2013-03-28 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2013-03-29 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -77.31 USD Expenses:Food:Groceries 77.31 USD 2013-03-30 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -34.86 USD Expenses:Food:Restaurant 34.86 USD 2013-04-04 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -24.37 USD Expenses:Food:Restaurant 24.37 USD 2013-04-04 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -68.99 USD Expenses:Food:Groceries 68.99 USD 2013-04-06 * "Jewel of Morroco" "Eating out alone" Liabilities:US:Chase:Slate -23.30 USD Expenses:Food:Restaurant 23.30 USD 2013-04-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 742.68 USD Assets:US:BofA:Checking -742.68 USD 2013-04-10 * "Jewel of Morroco" "Eating out with Natasha" Liabilities:US:Chase:Slate -14.51 USD Expenses:Food:Restaurant 14.51 USD 2013-04-10 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -62.61 USD Expenses:Food:Groceries 62.61 USD 2013-04-11 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -35.14 USD Expenses:Food:Restaurant 35.14 USD 2013-04-12 balance Liabilities:US:Chase:Slate -4129.18 USD 2013-04-16 * "Uncle Boons" "Eating out with work buddies" Liabilities:US:Chase:Slate -24.66 USD Expenses:Food:Restaurant 24.66 USD 2013-04-17 * "Rose Flower" "Eating out " Liabilities:US:Chase:Slate -29.12 USD Expenses:Food:Restaurant 29.12 USD 2013-04-22 * "Cafe Modagor" "Eating out with Julie" Liabilities:US:Chase:Slate -48.89 USD Expenses:Food:Restaurant 48.89 USD 2013-04-25 * "Kin Soy" "Eating out with Julie" Liabilities:US:Chase:Slate -39.78 USD Expenses:Food:Restaurant 39.78 USD 2013-04-26 * "Uncle Boons" "Eating out after work" Liabilities:US:Chase:Slate -53.69 USD Expenses:Food:Restaurant 53.69 USD 2013-04-28 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -132.80 USD Expenses:Food:Groceries 132.80 USD 2013-04-29 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2013-04-30 * "Chichipotle" "Eating out with Natasha" Liabilities:US:Chase:Slate -31.90 USD Expenses:Food:Restaurant 31.90 USD 2013-05-02 balance Liabilities:US:Chase:Slate -4610.02 USD 2013-05-05 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -32.63 USD Expenses:Food:Restaurant 32.63 USD 2013-05-05 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -71.51 USD Expenses:Food:Groceries 71.51 USD 2013-05-08 * "China Garden" "Eating out " Liabilities:US:Chase:Slate -19.41 USD Expenses:Food:Restaurant 19.41 USD 2013-05-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 736.02 USD Assets:US:BofA:Checking -736.02 USD 2013-05-13 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -33.88 USD Expenses:Food:Restaurant 33.88 USD 2013-05-17 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -30.34 USD Expenses:Food:Restaurant 30.34 USD 2013-05-18 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -77.09 USD Expenses:Food:Groceries 77.09 USD 2013-05-19 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -24.50 USD Expenses:Food:Restaurant 24.50 USD 2013-05-23 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -18.76 USD Expenses:Food:Restaurant 18.76 USD 2013-05-25 balance Liabilities:US:Chase:Slate -4182.12 USD 2013-05-28 * "Uncle Boons" "Eating out after work" Liabilities:US:Chase:Slate -35.55 USD Expenses:Food:Restaurant 35.55 USD 2013-05-29 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2013-06-01 event "location" "Chicago" 2013-06-02 * "Star of Siam" "" #trip-chicago-2013 Liabilities:US:Chase:Slate -26.17 USD Expenses:Food:Restaurant 26.17 USD 2013-06-03 * "25 Degrees Burger Bar" "" #trip-chicago-2013 Liabilities:US:Chase:Slate -21.05 USD Expenses:Food:Restaurant 21.05 USD 2013-06-03 * "Eataly Chicago" "" #trip-chicago-2013 Liabilities:US:Chase:Slate -46.90 USD Expenses:Food:Restaurant 46.90 USD 2013-06-03 * "Argo Tea" "" #trip-chicago-2013 Liabilities:US:Chase:Slate -6.73 USD Expenses:Food:Coffee 6.73 USD 2013-06-04 * "Star of Siam" "" #trip-chicago-2013 Liabilities:US:Chase:Slate -28.54 USD Expenses:Food:Restaurant 28.54 USD 2013-06-04 * "Another Sports Pub" "" #trip-chicago-2013 Liabilities:US:Chase:Slate -12.73 USD Expenses:Food:Alcohol 12.73 USD 2013-06-05 * "Argo Tea" "" #trip-chicago-2013 Liabilities:US:Chase:Slate -4.76 USD Expenses:Food:Coffee 4.76 USD 2013-06-06 * "25 Degrees Burger Bar" "" #trip-chicago-2013 Liabilities:US:Chase:Slate -22.97 USD Expenses:Food:Restaurant 22.97 USD 2013-06-06 * "Another Sports Pub" "" #trip-chicago-2013 Liabilities:US:Chase:Slate -12.71 USD Expenses:Food:Alcohol 12.71 USD 2013-06-07 * "Another Sports Pub" "" #trip-chicago-2013 Liabilities:US:Chase:Slate -12.01 USD Expenses:Food:Alcohol 12.01 USD 2013-06-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 348.32 USD Assets:US:BofA:Checking -348.32 USD 2013-06-08 * "25 Degrees Burger Bar" "" #trip-chicago-2013 Liabilities:US:Chase:Slate -23.59 USD Expenses:Food:Restaurant 23.59 USD 2013-06-08 * "Eataly Chicago" "" #trip-chicago-2013 Liabilities:US:Chase:Slate -16.84 USD Expenses:Food:Restaurant 16.84 USD 2013-06-08 * "Another Sports Pub" "" #trip-chicago-2013 Liabilities:US:Chase:Slate -12.22 USD Expenses:Food:Alcohol 12.22 USD 2013-06-09 * "Argo Tea" "" #trip-chicago-2013 Liabilities:US:Chase:Slate -5.48 USD Expenses:Food:Coffee 5.48 USD 2013-06-09 * "Consume vacation days" Assets:US:Hooli:Vacation -64 VACHR Expenses:Vacation 64 VACHR 2013-06-09 event "location" "New Metropolis" 2013-06-10 * "Goba Goba" "Eating out " Liabilities:US:Chase:Slate -42.08 USD Expenses:Food:Restaurant 42.08 USD 2013-06-10 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -53.67 USD Expenses:Food:Groceries 53.67 USD 2013-06-14 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -36.93 USD Expenses:Food:Restaurant 36.93 USD 2013-06-15 balance Liabilities:US:Chase:Slate -4374.73 USD 2013-06-16 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -41.90 USD Expenses:Food:Restaurant 41.90 USD 2013-06-21 * "Kin Soy" "Eating out after work" Liabilities:US:Chase:Slate -26.88 USD Expenses:Food:Restaurant 26.88 USD 2013-06-23 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -31.72 USD Expenses:Food:Restaurant 31.72 USD 2013-06-24 * "Rose Flower" "Eating out after work" Liabilities:US:Chase:Slate -57.68 USD Expenses:Food:Restaurant 57.68 USD 2013-06-26 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2013-06-27 * "Chichipotle" "Eating out after work" Liabilities:US:Chase:Slate -27.46 USD Expenses:Food:Restaurant 27.46 USD 2013-06-29 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -102.21 USD Expenses:Food:Groceries 102.21 USD 2013-06-30 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -22.06 USD Expenses:Food:Restaurant 22.06 USD 2013-07-03 * "Uncle Boons" "Eating out with Julie" Liabilities:US:Chase:Slate -31.83 USD Expenses:Food:Restaurant 31.83 USD 2013-07-05 * "Rose Flower" "Eating out after work" Liabilities:US:Chase:Slate -24.85 USD Expenses:Food:Restaurant 24.85 USD 2013-07-10 balance Liabilities:US:Chase:Slate -4861.32 USD 2013-07-10 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -49.21 USD Expenses:Food:Restaurant 49.21 USD 2013-07-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 626.40 USD Assets:US:BofA:Checking -626.40 USD 2013-07-13 * "Uncle Boons" "Eating out after work" Liabilities:US:Chase:Slate -28.65 USD Expenses:Food:Restaurant 28.65 USD 2013-07-15 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -66.62 USD Expenses:Food:Groceries 66.62 USD 2013-07-17 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -25.46 USD Expenses:Food:Restaurant 25.46 USD 2013-07-19 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -35.26 USD Expenses:Food:Restaurant 35.26 USD 2013-07-20 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -32.94 USD Expenses:Food:Restaurant 32.94 USD 2013-07-22 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -39.63 USD Expenses:Food:Restaurant 39.63 USD 2013-07-22 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -59.99 USD Expenses:Food:Groceries 59.99 USD 2013-07-26 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -48.44 USD Expenses:Food:Restaurant 48.44 USD 2013-07-27 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -20.73 USD Expenses:Food:Restaurant 20.73 USD 2013-07-28 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -58.98 USD Expenses:Food:Groceries 58.98 USD 2013-07-29 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2013-07-30 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -28.96 USD Expenses:Food:Restaurant 28.96 USD 2013-08-02 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -25.11 USD Expenses:Food:Restaurant 25.11 USD 2013-08-03 balance Liabilities:US:Chase:Slate -4874.90 USD 2013-08-03 * "China Garden" "Eating out with Natasha" Liabilities:US:Chase:Slate -22.58 USD Expenses:Food:Restaurant 22.58 USD 2013-08-05 * "Kin Soy" "Eating out with work buddies" Liabilities:US:Chase:Slate -40.09 USD Expenses:Food:Restaurant 40.09 USD 2013-08-09 * "Jewel of Morroco" "Eating out with Julie" Liabilities:US:Chase:Slate -32.84 USD Expenses:Food:Restaurant 32.84 USD 2013-08-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 686.28 USD Assets:US:BofA:Checking -686.28 USD 2013-08-10 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -61.53 USD Expenses:Food:Groceries 61.53 USD 2013-08-11 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -22.61 USD Expenses:Food:Restaurant 22.61 USD 2013-08-15 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -63.92 USD Expenses:Food:Restaurant 63.92 USD 2013-08-16 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -24.48 USD Expenses:Food:Restaurant 24.48 USD 2013-08-17 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -31.66 USD Expenses:Food:Restaurant 31.66 USD 2013-08-19 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -44.35 USD Expenses:Food:Restaurant 44.35 USD 2013-08-22 * "China Garden" "Eating out with Natasha" Liabilities:US:Chase:Slate -58.53 USD Expenses:Food:Restaurant 58.53 USD 2013-08-25 * "Uncle Boons" "Eating out with Bill" Liabilities:US:Chase:Slate -24.01 USD Expenses:Food:Restaurant 24.01 USD 2013-08-25 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -92.89 USD Expenses:Food:Groceries 92.89 USD 2013-08-26 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2013-08-29 * "Chichipotle" "Eating out after work" Liabilities:US:Chase:Slate -16.70 USD Expenses:Food:Restaurant 16.70 USD 2013-09-02 balance Liabilities:US:Chase:Slate -4844.81 USD 2013-09-02 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -95.95 USD Expenses:Food:Groceries 95.95 USD 2013-09-03 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -50.29 USD Expenses:Food:Restaurant 50.29 USD 2013-09-04 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -22.38 USD Expenses:Food:Restaurant 22.38 USD 2013-09-05 * "China Garden" "Eating out with Bill" Liabilities:US:Chase:Slate -43.71 USD Expenses:Food:Restaurant 43.71 USD 2013-09-07 * "Rose Flower" "Eating out with work buddies" Liabilities:US:Chase:Slate -17.53 USD Expenses:Food:Restaurant 17.53 USD 2013-09-09 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -67.81 USD Expenses:Food:Groceries 67.81 USD 2013-09-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 876.02 USD Assets:US:BofA:Checking -876.02 USD 2013-09-11 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -17.67 USD Expenses:Food:Restaurant 17.67 USD 2013-09-15 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -31.09 USD Expenses:Food:Restaurant 31.09 USD 2013-09-20 * "Kin Soy" "Eating out with Julie" Liabilities:US:Chase:Slate -30.76 USD Expenses:Food:Restaurant 30.76 USD 2013-09-20 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -79.96 USD Expenses:Food:Groceries 79.96 USD 2013-09-24 balance Liabilities:US:Chase:Slate -4425.94 USD 2013-09-24 * "Uncle Boons" "Eating out with work buddies" Liabilities:US:Chase:Slate -29.81 USD Expenses:Food:Restaurant 29.81 USD 2013-09-26 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2013-09-27 * "Goba Goba" "Eating out alone" Liabilities:US:Chase:Slate -23.96 USD Expenses:Food:Restaurant 23.96 USD 2013-09-30 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -21.95 USD Expenses:Food:Restaurant 21.95 USD 2013-10-03 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -31.48 USD Expenses:Food:Restaurant 31.48 USD 2013-10-04 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -22.66 USD Expenses:Food:Restaurant 22.66 USD 2013-10-04 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -98.28 USD Expenses:Food:Groceries 98.28 USD 2013-10-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 528.19 USD Assets:US:BofA:Checking -528.19 USD 2013-10-09 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -38.24 USD Expenses:Food:Restaurant 38.24 USD 2013-10-14 balance Liabilities:US:Chase:Slate -4284.13 USD 2013-10-14 * "Kin Soy" "Eating out with work buddies" Liabilities:US:Chase:Slate -16.53 USD Expenses:Food:Restaurant 16.53 USD 2013-10-16 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -33.19 USD Expenses:Food:Restaurant 33.19 USD 2013-10-21 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -13.61 USD Expenses:Food:Restaurant 13.61 USD 2013-10-24 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -58.99 USD Expenses:Food:Restaurant 58.99 USD 2013-10-24 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -106.66 USD Expenses:Food:Groceries 106.66 USD 2013-10-24 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2013-10-25 * "Uncle Boons" "Eating out after work" Liabilities:US:Chase:Slate -38.81 USD Expenses:Food:Restaurant 38.81 USD 2013-10-26 * "Rose Flower" "Eating out after work" Liabilities:US:Chase:Slate -50.94 USD Expenses:Food:Restaurant 50.94 USD 2013-10-28 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -46.54 USD Expenses:Food:Restaurant 46.54 USD 2013-11-02 * "Goba Goba" "Eating out " Liabilities:US:Chase:Slate -22.65 USD Expenses:Food:Restaurant 22.65 USD 2013-11-07 * "China Garden" "Eating out " Liabilities:US:Chase:Slate -31.92 USD Expenses:Food:Restaurant 31.92 USD 2013-11-08 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 583.33 USD Assets:US:BofA:Checking -583.33 USD 2013-11-11 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -43.49 USD Expenses:Food:Restaurant 43.49 USD 2013-11-11 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -68.75 USD Expenses:Food:Groceries 68.75 USD 2013-11-12 balance Liabilities:US:Chase:Slate -4352.88 USD 2013-11-16 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -29.96 USD Expenses:Food:Restaurant 29.96 USD 2013-11-21 * "Kin Soy" "Eating out with Bill" Liabilities:US:Chase:Slate -16.72 USD Expenses:Food:Restaurant 16.72 USD 2013-11-22 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2013-11-23 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -62.62 USD Expenses:Food:Groceries 62.62 USD 2013-11-24 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -23.51 USD Expenses:Food:Restaurant 23.51 USD 2013-11-27 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -29.52 USD Expenses:Food:Restaurant 29.52 USD 2013-12-02 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -17.93 USD Expenses:Food:Restaurant 17.93 USD 2013-12-03 balance Liabilities:US:Chase:Slate -4653.14 USD 2013-12-05 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -76.61 USD Expenses:Food:Groceries 76.61 USD 2013-12-06 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -58.75 USD Expenses:Food:Restaurant 58.75 USD 2013-12-07 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -49.45 USD Expenses:Food:Restaurant 49.45 USD 2013-12-08 event "location" "Los Angeles" 2013-12-08 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 577.98 USD Assets:US:BofA:Checking -577.98 USD 2013-12-09 * "Mr. Marcel" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -53.74 USD Expenses:Food:Restaurant 53.74 USD 2013-12-10 * "Starbucks" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -6.80 USD Expenses:Food:Coffee 6.80 USD 2013-12-11 * "Banana Leaf" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -20.31 USD Expenses:Food:Restaurant 20.31 USD 2013-12-11 * "Dupar's" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -32.49 USD Expenses:Food:Restaurant 32.49 USD 2013-12-11 * "Starbucks" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -5.85 USD Expenses:Food:Coffee 5.85 USD 2013-12-11 * "E.B.'s Beer and Wine" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -11.95 USD Expenses:Food:Alcohol 11.95 USD 2013-12-12 * "Pampas Grill" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -22.57 USD Expenses:Food:Restaurant 22.57 USD 2013-12-13 * "Chipotle" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -16.74 USD Expenses:Food:Restaurant 16.74 USD 2013-12-14 * "Dupar's" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -29.48 USD Expenses:Food:Restaurant 29.48 USD 2013-12-14 * "Pampas Grill" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -25.23 USD Expenses:Food:Restaurant 25.23 USD 2013-12-14 * "Chipotle" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -15.16 USD Expenses:Food:Restaurant 15.16 USD 2013-12-14 * "Starbucks" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -6.87 USD Expenses:Food:Coffee 6.87 USD 2013-12-14 * "E.B.'s Beer and Wine" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -9.88 USD Expenses:Food:Alcohol 9.88 USD 2013-12-15 * "Pampas Grill" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -26.70 USD Expenses:Food:Restaurant 26.70 USD 2013-12-16 * "Mr. Marcel" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -29.29 USD Expenses:Food:Restaurant 29.29 USD 2013-12-16 * "Chipotle" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -15.90 USD Expenses:Food:Restaurant 15.90 USD 2013-12-17 * "Pampas Grill" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -24.30 USD Expenses:Food:Restaurant 24.30 USD 2013-12-18 * "Banana Leaf" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -27.84 USD Expenses:Food:Restaurant 27.84 USD 2013-12-18 * "E.B.'s Beer and Wine" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -11.00 USD Expenses:Food:Alcohol 11.00 USD 2013-12-19 * "Chipotle" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -16.97 USD Expenses:Food:Restaurant 16.97 USD 2013-12-19 * "Starbucks" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -6.37 USD Expenses:Food:Coffee 6.37 USD 2013-12-19 * "E.B.'s Beer and Wine" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -5.29 USD Expenses:Food:Alcohol 5.29 USD 2013-12-21 * "Mr. Marcel" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -50.55 USD Expenses:Food:Restaurant 50.55 USD 2013-12-21 * "Banana Leaf" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -22.25 USD Expenses:Food:Restaurant 22.25 USD 2013-12-21 * "Dupar's" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -25.71 USD Expenses:Food:Restaurant 25.71 USD 2013-12-23 balance Liabilities:US:Chase:Slate -4779.21 USD 2013-12-23 * "Pampas Grill" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -28.15 USD Expenses:Food:Restaurant 28.15 USD 2013-12-23 * "Chipotle" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -14.43 USD Expenses:Food:Restaurant 14.43 USD 2013-12-24 * "Chipotle" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -17.53 USD Expenses:Food:Restaurant 17.53 USD 2013-12-25 * "Starbucks" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -6.56 USD Expenses:Food:Coffee 6.56 USD 2013-12-25 * "E.B.'s Beer and Wine" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -7.38 USD Expenses:Food:Alcohol 7.38 USD 2013-12-26 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -24.16 USD Expenses:Food:Restaurant 24.16 USD 2013-12-26 * "Banana Leaf" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -21.85 USD Expenses:Food:Restaurant 21.85 USD 2013-12-26 * "Pampas Grill" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -28.35 USD Expenses:Food:Restaurant 28.35 USD 2013-12-26 * "Starbucks" "" #trip-los-angeles-2013 Liabilities:US:Chase:Slate -6.76 USD Expenses:Food:Coffee 6.76 USD 2013-12-26 * "Consume vacation days" Assets:US:Hooli:Vacation -144 VACHR Expenses:Vacation 144 VACHR 2013-12-26 event "location" "New Metropolis" 2013-12-31 * "Rose Flower" "Eating out " Liabilities:US:Chase:Slate -41.24 USD Expenses:Food:Restaurant 41.24 USD 2014-01-01 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -91.36 USD Expenses:Food:Groceries 91.36 USD 2014-01-04 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -22.59 USD Expenses:Food:Restaurant 22.59 USD 2014-01-06 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -60.46 USD Expenses:Food:Groceries 60.46 USD 2014-01-09 * "Rose Flower" "Eating out " Liabilities:US:Chase:Slate -65.48 USD Expenses:Food:Restaurant 65.48 USD 2014-01-09 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 281.13 USD Assets:US:BofA:Checking -281.13 USD 2014-01-13 * "Chichipotle" "Eating out with Natasha" Liabilities:US:Chase:Slate -25.80 USD Expenses:Food:Restaurant 25.80 USD 2014-01-15 * "Chichipotle" "Eating out after work" Liabilities:US:Chase:Slate -36.27 USD Expenses:Food:Restaurant 36.27 USD 2014-01-16 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -21.97 USD Expenses:Food:Restaurant 21.97 USD 2014-01-17 balance Liabilities:US:Chase:Slate -5018.42 USD 2014-01-18 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -29.08 USD Expenses:Food:Restaurant 29.08 USD 2014-01-19 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -40.56 USD Expenses:Food:Restaurant 40.56 USD 2014-01-21 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2014-01-22 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -55.81 USD Expenses:Food:Restaurant 55.81 USD 2014-01-22 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -70.95 USD Expenses:Food:Groceries 70.95 USD 2014-01-27 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -26.60 USD Expenses:Food:Restaurant 26.60 USD 2014-02-01 * "Kin Soy" "Eating out with Julie" Liabilities:US:Chase:Slate -19.99 USD Expenses:Food:Restaurant 19.99 USD 2014-02-02 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -37.81 USD Expenses:Food:Restaurant 37.81 USD 2014-02-06 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -32.53 USD Expenses:Food:Restaurant 32.53 USD 2014-02-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 585.52 USD Assets:US:BofA:Checking -585.52 USD 2014-02-08 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -68.15 USD Expenses:Food:Groceries 68.15 USD 2014-02-10 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -16.36 USD Expenses:Food:Restaurant 16.36 USD 2014-02-14 balance Liabilities:US:Chase:Slate -4950.74 USD 2014-02-14 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -42.32 USD Expenses:Food:Restaurant 42.32 USD 2014-02-18 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -32.08 USD Expenses:Food:Restaurant 32.08 USD 2014-02-22 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -39.46 USD Expenses:Food:Restaurant 39.46 USD 2014-02-23 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -42.96 USD Expenses:Food:Restaurant 42.96 USD 2014-02-23 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2014-02-26 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -30.37 USD Expenses:Food:Restaurant 30.37 USD 2014-02-26 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -68.66 USD Expenses:Food:Groceries 68.66 USD 2014-03-02 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -21.98 USD Expenses:Food:Restaurant 21.98 USD 2014-03-06 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -36.40 USD Expenses:Food:Restaurant 36.40 USD 2014-03-08 * "Jewel of Morroco" "Eating out with work buddies" Liabilities:US:Chase:Slate -49.60 USD Expenses:Food:Restaurant 49.60 USD 2014-03-09 balance Liabilities:US:Chase:Slate -5434.57 USD 2014-03-11 * "Rose Flower" "Eating out with work buddies" Liabilities:US:Chase:Slate -75.68 USD Expenses:Food:Restaurant 75.68 USD 2014-03-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 575.87 USD Assets:US:BofA:Checking -575.87 USD 2014-03-12 * "Jewel of Morroco" "Eating out with work buddies" Liabilities:US:Chase:Slate -39.92 USD Expenses:Food:Restaurant 39.92 USD 2014-03-14 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -49.67 USD Expenses:Food:Groceries 49.67 USD 2014-03-15 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -22.05 USD Expenses:Food:Restaurant 22.05 USD 2014-03-16 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -34.77 USD Expenses:Food:Restaurant 34.77 USD 2014-03-21 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -32.88 USD Expenses:Food:Restaurant 32.88 USD 2014-03-23 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -56.78 USD Expenses:Food:Restaurant 56.78 USD 2014-03-23 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -60.38 USD Expenses:Food:Groceries 60.38 USD 2014-03-27 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -19.01 USD Expenses:Food:Restaurant 19.01 USD 2014-03-28 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2014-03-31 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -38.19 USD Expenses:Food:Restaurant 38.19 USD 2014-04-01 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -26.25 USD Expenses:Food:Restaurant 26.25 USD 2014-04-04 balance Liabilities:US:Chase:Slate -5434.28 USD 2014-04-06 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -23.13 USD Expenses:Food:Restaurant 23.13 USD 2014-04-08 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -47.29 USD Expenses:Food:Groceries 47.29 USD 2014-04-11 * "Uncle Boons" "Eating out after work" Liabilities:US:Chase:Slate -52.29 USD Expenses:Food:Restaurant 52.29 USD 2014-04-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 622.61 USD Assets:US:BofA:Checking -622.61 USD 2014-04-15 * "Chichipotle" "Eating out alone" Liabilities:US:Chase:Slate -34.80 USD Expenses:Food:Restaurant 34.80 USD 2014-04-16 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -125.20 USD Expenses:Food:Groceries 125.20 USD 2014-04-18 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -22.58 USD Expenses:Food:Restaurant 22.58 USD 2014-04-19 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -41.78 USD Expenses:Food:Restaurant 41.78 USD 2014-04-20 * "Uncle Boons" "Eating out with Julie" Liabilities:US:Chase:Slate -33.04 USD Expenses:Food:Restaurant 33.04 USD 2014-04-22 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -94.94 USD Expenses:Food:Groceries 94.94 USD 2014-04-25 * "Jewel of Morroco" "Eating out with work buddies" Liabilities:US:Chase:Slate -44.64 USD Expenses:Food:Restaurant 44.64 USD 2014-04-28 * "Chichipotle" "Eating out with work buddies" Liabilities:US:Chase:Slate -54.07 USD Expenses:Food:Restaurant 54.07 USD 2014-04-28 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2014-05-03 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -37.55 USD Expenses:Food:Restaurant 37.55 USD 2014-05-04 balance Liabilities:US:Chase:Slate -5542.98 USD 2014-05-06 * "Cafe Modagor" "Eating out with Bill" Liabilities:US:Chase:Slate -15.21 USD Expenses:Food:Restaurant 15.21 USD 2014-05-08 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -23.10 USD Expenses:Food:Restaurant 23.10 USD 2014-05-10 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -78.55 USD Expenses:Food:Groceries 78.55 USD 2014-05-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 751.77 USD Assets:US:BofA:Checking -751.77 USD 2014-05-12 * "Kin Soy" "Eating out with Bill" Liabilities:US:Chase:Slate -26.31 USD Expenses:Food:Restaurant 26.31 USD 2014-05-16 * "Uncle Boons" "Eating out " Liabilities:US:Chase:Slate -17.52 USD Expenses:Food:Restaurant 17.52 USD 2014-05-21 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -20.98 USD Expenses:Food:Restaurant 20.98 USD 2014-05-21 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -56.68 USD Expenses:Food:Groceries 56.68 USD 2014-05-24 * "China Garden" "Eating out " Liabilities:US:Chase:Slate -38.41 USD Expenses:Food:Restaurant 38.41 USD 2014-05-25 * "China Garden" "Eating out " Liabilities:US:Chase:Slate -22.01 USD Expenses:Food:Restaurant 22.01 USD 2014-05-27 * "Kin Soy" "Eating out after work" Liabilities:US:Chase:Slate -25.09 USD Expenses:Food:Restaurant 25.09 USD 2014-05-31 * "Uncle Boons" "Eating out " Liabilities:US:Chase:Slate -34.95 USD Expenses:Food:Restaurant 34.95 USD 2014-05-31 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2014-06-02 balance Liabilities:US:Chase:Slate -5270.02 USD 2014-06-05 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -44.62 USD Expenses:Food:Restaurant 44.62 USD 2014-06-05 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -63.13 USD Expenses:Food:Groceries 63.13 USD 2014-06-08 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 460.53 USD Assets:US:BofA:Checking -460.53 USD 2014-06-09 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -17.14 USD Expenses:Food:Restaurant 17.14 USD 2014-06-12 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -28.04 USD Expenses:Food:Restaurant 28.04 USD 2014-06-16 * "Jewel of Morroco" "Eating out with Bill" Liabilities:US:Chase:Slate -24.34 USD Expenses:Food:Restaurant 24.34 USD 2014-06-20 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -36.27 USD Expenses:Food:Restaurant 36.27 USD 2014-06-24 * "Rose Flower" "Eating out with Natasha" Liabilities:US:Chase:Slate -18.62 USD Expenses:Food:Restaurant 18.62 USD 2014-06-25 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -70.92 USD Expenses:Food:Groceries 70.92 USD 2014-06-27 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2014-06-29 balance Liabilities:US:Chase:Slate -5232.57 USD 2014-06-29 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -19.89 USD Expenses:Food:Restaurant 19.89 USD 2014-07-03 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -35.60 USD Expenses:Food:Restaurant 35.60 USD 2014-07-04 * "China Garden" "Eating out with work buddies" Liabilities:US:Chase:Slate -42.40 USD Expenses:Food:Restaurant 42.40 USD 2014-07-06 * "Rose Flower" "Eating out with work buddies" Liabilities:US:Chase:Slate -13.53 USD Expenses:Food:Restaurant 13.53 USD 2014-07-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 485.26 USD Assets:US:BofA:Checking -485.26 USD 2014-07-09 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -75.65 USD Expenses:Food:Groceries 75.65 USD 2014-07-11 * "Kin Soy" "Eating out with Bill" Liabilities:US:Chase:Slate -45.92 USD Expenses:Food:Restaurant 45.92 USD 2014-07-16 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -33.99 USD Expenses:Food:Restaurant 33.99 USD 2014-07-21 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -14.87 USD Expenses:Food:Restaurant 14.87 USD 2014-07-22 balance Liabilities:US:Chase:Slate -5029.16 USD 2014-07-22 * "China Garden" "Eating out with Bill" Liabilities:US:Chase:Slate -14.83 USD Expenses:Food:Restaurant 14.83 USD 2014-07-24 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2014-07-26 * "Goba Goba" "Eating out with Natasha" Liabilities:US:Chase:Slate -26.45 USD Expenses:Food:Restaurant 26.45 USD 2014-07-28 * "China Garden" "Eating out with Natasha" Liabilities:US:Chase:Slate -72.82 USD Expenses:Food:Restaurant 72.82 USD 2014-07-28 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -98.02 USD Expenses:Food:Groceries 98.02 USD 2014-08-02 * "Uncle Boons" "Eating out with work buddies" Liabilities:US:Chase:Slate -14.65 USD Expenses:Food:Restaurant 14.65 USD 2014-08-05 * "Rose Flower" "Eating out with work buddies" Liabilities:US:Chase:Slate -38.92 USD Expenses:Food:Restaurant 38.92 USD 2014-08-05 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -157.54 USD Expenses:Food:Groceries 157.54 USD 2014-08-08 * "Cafe Modagor" "Eating out with Bill" Liabilities:US:Chase:Slate -47.49 USD Expenses:Food:Restaurant 47.49 USD 2014-08-11 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -21.37 USD Expenses:Food:Restaurant 21.37 USD 2014-08-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 706.87 USD Assets:US:BofA:Checking -706.87 USD 2014-08-12 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -27.08 USD Expenses:Food:Restaurant 27.08 USD 2014-08-14 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -27.94 USD Expenses:Food:Restaurant 27.94 USD 2014-08-14 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -120.17 USD Expenses:Food:Groceries 120.17 USD 2014-08-16 * "Rose Flower" "Eating out with Joe" Liabilities:US:Chase:Slate -22.94 USD Expenses:Food:Restaurant 22.94 USD 2014-08-18 balance Liabilities:US:Chase:Slate -5132.51 USD 2014-08-19 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -29.29 USD Expenses:Food:Restaurant 29.29 USD 2014-08-23 * "China Garden" "Eating out " Liabilities:US:Chase:Slate -31.67 USD Expenses:Food:Restaurant 31.67 USD 2014-08-23 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2014-08-26 * "China Garden" "Eating out after work" Liabilities:US:Chase:Slate -24.06 USD Expenses:Food:Restaurant 24.06 USD 2014-08-29 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -31.82 USD Expenses:Food:Restaurant 31.82 USD 2014-08-30 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -91.17 USD Expenses:Food:Groceries 91.17 USD 2014-09-02 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -32.94 USD Expenses:Food:Restaurant 32.94 USD 2014-09-03 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -22.76 USD Expenses:Food:Restaurant 22.76 USD 2014-09-07 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -35.00 USD Expenses:Food:Restaurant 35.00 USD 2014-09-08 balance Liabilities:US:Chase:Slate -5551.22 USD 2014-09-10 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -26.73 USD Expenses:Food:Restaurant 26.73 USD 2014-09-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 643.57 USD Assets:US:BofA:Checking -643.57 USD 2014-09-14 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -24.93 USD Expenses:Food:Restaurant 24.93 USD 2014-09-17 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -101.88 USD Expenses:Food:Groceries 101.88 USD 2014-09-19 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -24.85 USD Expenses:Food:Restaurant 24.85 USD 2014-09-24 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -50.13 USD Expenses:Food:Restaurant 50.13 USD 2014-09-24 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2014-09-26 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -28.16 USD Expenses:Food:Restaurant 28.16 USD 2014-09-28 * "Goba Goba" "Eating out with Joe" Liabilities:US:Chase:Slate -44.67 USD Expenses:Food:Restaurant 44.67 USD 2014-09-30 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -24.21 USD Expenses:Food:Restaurant 24.21 USD 2014-10-01 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -43.51 USD Expenses:Food:Restaurant 43.51 USD 2014-10-01 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -62.75 USD Expenses:Food:Groceries 62.75 USD 2014-10-04 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -36.25 USD Expenses:Food:Restaurant 36.25 USD 2014-10-06 balance Liabilities:US:Chase:Slate -5495.72 USD 2014-10-06 * "Jewel of Morroco" "Eating out with work buddies" Liabilities:US:Chase:Slate -53.26 USD Expenses:Food:Restaurant 53.26 USD 2014-10-07 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -44.92 USD Expenses:Food:Restaurant 44.92 USD 2014-10-11 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -30.87 USD Expenses:Food:Restaurant 30.87 USD 2014-10-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 690.39 USD Assets:US:BofA:Checking -690.39 USD 2014-10-14 * "Jewel of Morroco" "Eating out with Julie" Liabilities:US:Chase:Slate -27.06 USD Expenses:Food:Restaurant 27.06 USD 2014-10-15 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -39.68 USD Expenses:Food:Restaurant 39.68 USD 2014-10-18 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -108.05 USD Expenses:Food:Groceries 108.05 USD 2014-10-20 * "Uncle Boons" "Eating out " Liabilities:US:Chase:Slate -17.50 USD Expenses:Food:Restaurant 17.50 USD 2014-10-23 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -103.59 USD Expenses:Food:Groceries 103.59 USD 2014-10-25 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -30.46 USD Expenses:Food:Restaurant 30.46 USD 2014-10-25 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2014-10-28 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -39.51 USD Expenses:Food:Restaurant 39.51 USD 2014-10-29 balance Liabilities:US:Chase:Slate -5420.23 USD 2014-10-31 * "Uncle Boons" "Eating out with Bill" Liabilities:US:Chase:Slate -23.51 USD Expenses:Food:Restaurant 23.51 USD 2014-11-01 event "location" "Los Angeles" 2014-11-02 * "Mr. Marcel" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -30.47 USD Expenses:Food:Restaurant 30.47 USD 2014-11-02 * "Dupar's" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -25.62 USD Expenses:Food:Restaurant 25.62 USD 2014-11-02 * "Starbucks" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -5.59 USD Expenses:Food:Coffee 5.59 USD 2014-11-03 * "Mr. Marcel" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -31.94 USD Expenses:Food:Restaurant 31.94 USD 2014-11-03 * "Chipotle" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -14.55 USD Expenses:Food:Restaurant 14.55 USD 2014-11-03 * "Starbucks" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -6.02 USD Expenses:Food:Coffee 6.02 USD 2014-11-04 * "Banana Leaf" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -25.20 USD Expenses:Food:Restaurant 25.20 USD 2014-11-04 * "Chipotle" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -17.98 USD Expenses:Food:Restaurant 17.98 USD 2014-11-05 * "Banana Leaf" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -23.72 USD Expenses:Food:Restaurant 23.72 USD 2014-11-05 * "Chipotle" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -14.39 USD Expenses:Food:Restaurant 14.39 USD 2014-11-06 * "Banana Leaf" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -25.02 USD Expenses:Food:Restaurant 25.02 USD 2014-11-07 * "Pampas Grill" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -33.65 USD Expenses:Food:Restaurant 33.65 USD 2014-11-07 * "Starbucks" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -7.20 USD Expenses:Food:Coffee 7.20 USD 2014-11-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 538.62 USD Assets:US:BofA:Checking -538.62 USD 2014-11-08 * "Dupar's" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -27.47 USD Expenses:Food:Restaurant 27.47 USD 2014-11-08 * "Pampas Grill" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -25.95 USD Expenses:Food:Restaurant 25.95 USD 2014-11-08 * "Starbucks" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -4.35 USD Expenses:Food:Coffee 4.35 USD 2014-11-09 * "E.B.'s Beer and Wine" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -9.07 USD Expenses:Food:Alcohol 9.07 USD 2014-11-10 * "Banana Leaf" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -25.93 USD Expenses:Food:Restaurant 25.93 USD 2014-11-10 * "Chipotle" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -15.23 USD Expenses:Food:Restaurant 15.23 USD 2014-11-10 * "Starbucks" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -5.37 USD Expenses:Food:Coffee 5.37 USD 2014-11-11 * "Dupar's" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -36.47 USD Expenses:Food:Restaurant 36.47 USD 2014-11-13 * "Chipotle" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -14.01 USD Expenses:Food:Restaurant 14.01 USD 2014-11-13 * "Starbucks" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -6.44 USD Expenses:Food:Coffee 6.44 USD 2014-11-13 * "E.B.'s Beer and Wine" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -7.04 USD Expenses:Food:Alcohol 7.04 USD 2014-11-14 * "Banana Leaf" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -26.25 USD Expenses:Food:Restaurant 26.25 USD 2014-11-14 * "Dupar's" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -32.03 USD Expenses:Food:Restaurant 32.03 USD 2014-11-14 * "Chipotle" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -16.14 USD Expenses:Food:Restaurant 16.14 USD 2014-11-15 * "Mr. Marcel" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -31.25 USD Expenses:Food:Restaurant 31.25 USD 2014-11-15 * "Banana Leaf" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -28.68 USD Expenses:Food:Restaurant 28.68 USD 2014-11-15 * "Dupar's" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -33.08 USD Expenses:Food:Restaurant 33.08 USD 2014-11-15 * "Pampas Grill" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -24.22 USD Expenses:Food:Restaurant 24.22 USD 2014-11-15 * "E.B.'s Beer and Wine" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -10.29 USD Expenses:Food:Alcohol 10.29 USD 2014-11-16 * "Banana Leaf" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -24.98 USD Expenses:Food:Restaurant 24.98 USD 2014-11-17 * "Mr. Marcel" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -35.71 USD Expenses:Food:Restaurant 35.71 USD 2014-11-17 * "E.B.'s Beer and Wine" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -9.07 USD Expenses:Food:Alcohol 9.07 USD 2014-11-18 * "Pampas Grill" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -25.96 USD Expenses:Food:Restaurant 25.96 USD 2014-11-19 * "Banana Leaf" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -24.67 USD Expenses:Food:Restaurant 24.67 USD 2014-11-20 * "Banana Leaf" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -29.56 USD Expenses:Food:Restaurant 29.56 USD 2014-11-20 * "Starbucks" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -5.87 USD Expenses:Food:Coffee 5.87 USD 2014-11-22 * "Dupar's" "" #trip-los-angeles-2014 Liabilities:US:Chase:Slate -34.91 USD Expenses:Food:Restaurant 34.91 USD 2014-11-22 * "Consume vacation days" Assets:US:Hooli:Vacation -168 VACHR Expenses:Vacation 168 VACHR 2014-11-22 event "location" "New Metropolis" 2014-11-23 balance Liabilities:US:Chase:Slate -5736.47 USD 2014-11-23 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -29.26 USD Expenses:Food:Restaurant 29.26 USD 2014-11-23 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -54.80 USD Expenses:Food:Groceries 54.80 USD 2014-11-26 * "China Garden" "Eating out with Julie" Liabilities:US:Chase:Slate -20.82 USD Expenses:Food:Restaurant 20.82 USD 2014-11-30 * "Cafe Modagor" "Eating out with Natasha" Liabilities:US:Chase:Slate -18.32 USD Expenses:Food:Restaurant 18.32 USD 2014-12-03 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -28.11 USD Expenses:Food:Restaurant 28.11 USD 2014-12-04 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -33.80 USD Expenses:Food:Restaurant 33.80 USD 2014-12-08 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -26.61 USD Expenses:Food:Restaurant 26.61 USD 2014-12-09 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -120.98 USD Expenses:Food:Groceries 120.98 USD 2014-12-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 324.96 USD Assets:US:BofA:Checking -324.96 USD 2014-12-11 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -21.52 USD Expenses:Food:Restaurant 21.52 USD 2014-12-16 * "Uncle Boons" "Eating out with Bill" Liabilities:US:Chase:Slate -27.48 USD Expenses:Food:Restaurant 27.48 USD 2014-12-16 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -50.69 USD Expenses:Food:Groceries 50.69 USD 2014-12-18 * "China Garden" "Eating out with Julie" Liabilities:US:Chase:Slate -15.56 USD Expenses:Food:Restaurant 15.56 USD 2014-12-20 balance Liabilities:US:Chase:Slate -5859.46 USD 2014-12-20 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2014-12-22 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -14.70 USD Expenses:Food:Restaurant 14.70 USD 2014-12-23 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -34.11 USD Expenses:Food:Restaurant 34.11 USD 2014-12-25 * "Kin Soy" "Eating out with Bill" Liabilities:US:Chase:Slate -34.20 USD Expenses:Food:Restaurant 34.20 USD 2014-12-28 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -34.36 USD Expenses:Food:Restaurant 34.36 USD 2014-12-29 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -31.84 USD Expenses:Food:Restaurant 31.84 USD 2015-01-02 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -24.24 USD Expenses:Food:Restaurant 24.24 USD 2015-01-03 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -16.90 USD Expenses:Food:Restaurant 16.90 USD 2015-01-05 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -93.83 USD Expenses:Food:Groceries 93.83 USD 2015-01-06 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -24.23 USD Expenses:Food:Restaurant 24.23 USD 2015-01-08 * "Uncle Boons" "Eating out with Bill" Liabilities:US:Chase:Slate -39.91 USD Expenses:Food:Restaurant 39.91 USD 2015-01-11 balance Liabilities:US:Chase:Slate -6327.78 USD 2015-01-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 592.62 USD Assets:US:BofA:Checking -592.62 USD 2015-01-13 * "Rose Flower" "Eating out with Julie" Liabilities:US:Chase:Slate -30.57 USD Expenses:Food:Restaurant 30.57 USD 2015-01-15 * "Chichipotle" "Eating out with Natasha" Liabilities:US:Chase:Slate -28.75 USD Expenses:Food:Restaurant 28.75 USD 2015-01-19 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-01-20 * "Kin Soy" "Eating out with Julie" Liabilities:US:Chase:Slate -38.27 USD Expenses:Food:Restaurant 38.27 USD 2015-01-22 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -39.59 USD Expenses:Food:Restaurant 39.59 USD 2015-01-22 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -77.47 USD Expenses:Food:Groceries 77.47 USD 2015-01-26 * "Kin Soy" "Eating out after work" Liabilities:US:Chase:Slate -17.05 USD Expenses:Food:Restaurant 17.05 USD 2015-01-29 * "Chichipotle" "Eating out with Joe" Liabilities:US:Chase:Slate -22.33 USD Expenses:Food:Restaurant 22.33 USD 2015-02-01 balance Liabilities:US:Chase:Slate -6109.19 USD 2015-02-01 * "Chichipotle" "Eating out with Bill" Liabilities:US:Chase:Slate -24.61 USD Expenses:Food:Restaurant 24.61 USD 2015-02-04 * "Jewel of Morroco" "Eating out with Natasha" Liabilities:US:Chase:Slate -22.12 USD Expenses:Food:Restaurant 22.12 USD 2015-02-06 * "China Garden" "Eating out with Julie" Liabilities:US:Chase:Slate -18.06 USD Expenses:Food:Restaurant 18.06 USD 2015-02-07 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -99.69 USD Expenses:Food:Groceries 99.69 USD 2015-02-08 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -37.33 USD Expenses:Food:Restaurant 37.33 USD 2015-02-10 * "Goba Goba" "Eating out " Liabilities:US:Chase:Slate -30.82 USD Expenses:Food:Restaurant 30.82 USD 2015-02-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 576.09 USD Assets:US:BofA:Checking -576.09 USD 2015-02-12 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -28.62 USD Expenses:Food:Restaurant 28.62 USD 2015-02-14 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -103.00 USD Expenses:Food:Groceries 103.00 USD 2015-02-15 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-02-16 * "Rose Flower" "Eating out with Bill" Liabilities:US:Chase:Slate -50.49 USD Expenses:Food:Restaurant 50.49 USD 2015-02-17 * "China Garden" "Eating out with work buddies" Liabilities:US:Chase:Slate -29.86 USD Expenses:Food:Restaurant 29.86 USD 2015-02-20 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -28.88 USD Expenses:Food:Restaurant 28.88 USD 2015-02-21 * "Good Moods Market" "Buying groceries" Liabilities:US:Chase:Slate -93.91 USD Expenses:Food:Groceries 93.91 USD 2015-02-22 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -23.01 USD Expenses:Food:Restaurant 23.01 USD 2015-02-24 balance Liabilities:US:Chase:Slate -6243.50 USD 2015-02-26 * "Kin Soy" "Eating out alone" Liabilities:US:Chase:Slate -44.24 USD Expenses:Food:Restaurant 44.24 USD 2015-02-28 * "Uncle Boons" "Eating out after work" Liabilities:US:Chase:Slate -27.52 USD Expenses:Food:Restaurant 27.52 USD 2015-03-01 * "Goba Goba" "Eating out " Liabilities:US:Chase:Slate -24.63 USD Expenses:Food:Restaurant 24.63 USD 2015-03-06 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -22.53 USD Expenses:Food:Restaurant 22.53 USD 2015-03-10 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 634.23 USD Assets:US:BofA:Checking -634.23 USD 2015-03-11 * "Kin Soy" "Eating out with Bill" Liabilities:US:Chase:Slate -37.54 USD Expenses:Food:Restaurant 37.54 USD 2015-03-11 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -71.42 USD Expenses:Food:Groceries 71.42 USD 2015-03-16 * "Rose Flower" "Eating out after work" Liabilities:US:Chase:Slate -51.57 USD Expenses:Food:Restaurant 51.57 USD 2015-03-16 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-03-20 balance Liabilities:US:Chase:Slate -6008.72 USD 2015-03-21 * "China Garden" "Eating out with Julie" Liabilities:US:Chase:Slate -36.50 USD Expenses:Food:Restaurant 36.50 USD 2015-03-25 * "Chichipotle" "Eating out after work" Liabilities:US:Chase:Slate -25.85 USD Expenses:Food:Restaurant 25.85 USD 2015-03-29 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -55.66 USD Expenses:Food:Restaurant 55.66 USD 2015-03-29 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -136.08 USD Expenses:Food:Groceries 136.08 USD 2015-04-03 * "China Garden" "Eating out alone" Liabilities:US:Chase:Slate -23.53 USD Expenses:Food:Restaurant 23.53 USD 2015-04-06 * "Cafe Modagor" "Eating out after work" Liabilities:US:Chase:Slate -46.38 USD Expenses:Food:Restaurant 46.38 USD 2015-04-06 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -53.93 USD Expenses:Food:Groceries 53.93 USD 2015-04-07 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -11.25 USD Expenses:Food:Restaurant 11.25 USD 2015-04-08 event "location" "New York" 2015-04-09 balance Liabilities:US:Chase:Slate -6397.90 USD 2015-04-09 * "Laut" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -27.08 USD Expenses:Food:Restaurant 27.08 USD 2015-04-09 * "Gimme! Coffee" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -5.76 USD Expenses:Food:Coffee 5.76 USD 2015-04-10 * "Uncle Boons" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -43.92 USD Expenses:Food:Restaurant 43.92 USD 2015-04-10 * "Gimme! Coffee" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -6.30 USD Expenses:Food:Coffee 6.30 USD 2015-04-11 * "Cafe Select" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -23.06 USD Expenses:Food:Restaurant 23.06 USD 2015-04-11 * "Laut" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -31.22 USD Expenses:Food:Restaurant 31.22 USD 2015-04-11 * "La Colombe" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -7.06 USD Expenses:Food:Coffee 7.06 USD 2015-04-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 659.74 USD Assets:US:BofA:Checking -659.74 USD 2015-04-12 * "Cafe Select" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -35.21 USD Expenses:Food:Restaurant 35.21 USD 2015-04-13 * "Takahachi" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -51.81 USD Expenses:Food:Restaurant 51.81 USD 2015-04-15 * "Cafe Select" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -26.80 USD Expenses:Food:Restaurant 26.80 USD 2015-04-15 * "Takahachi" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -50.62 USD Expenses:Food:Restaurant 50.62 USD 2015-04-15 * "Laut" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -25.61 USD Expenses:Food:Restaurant 25.61 USD 2015-04-15 * "Gimme! Coffee" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -6.33 USD Expenses:Food:Coffee 6.33 USD 2015-04-16 * "Takahachi" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -52.06 USD Expenses:Food:Restaurant 52.06 USD 2015-04-16 * "Gimme! Coffee" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -4.35 USD Expenses:Food:Coffee 4.35 USD 2015-04-17 * "Uncle Boons" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -45.52 USD Expenses:Food:Restaurant 45.52 USD 2015-04-17 * "Gimme! Coffee" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -8.43 USD Expenses:Food:Coffee 8.43 USD 2015-04-18 * "Uncle Boons" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -39.40 USD Expenses:Food:Restaurant 39.40 USD 2015-04-18 * "Takahachi" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -50.79 USD Expenses:Food:Restaurant 50.79 USD 2015-04-19 * "Cafe Select" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -27.04 USD Expenses:Food:Restaurant 27.04 USD 2015-04-19 * "La Colombe" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -4.29 USD Expenses:Food:Coffee 4.29 USD 2015-04-20 * "Cafe Select" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -28.52 USD Expenses:Food:Restaurant 28.52 USD 2015-04-20 * "Takahachi" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -46.43 USD Expenses:Food:Restaurant 46.43 USD 2015-04-21 * "Takahachi" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -48.14 USD Expenses:Food:Restaurant 48.14 USD 2015-04-21 * "Laut" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -28.99 USD Expenses:Food:Restaurant 28.99 USD 2015-04-21 * "Gimme! Coffee" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -5.44 USD Expenses:Food:Coffee 5.44 USD 2015-04-22 * "Cafe Select" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -27.90 USD Expenses:Food:Restaurant 27.90 USD 2015-04-22 * "Takahachi" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -57.71 USD Expenses:Food:Restaurant 57.71 USD 2015-04-23 * "Cafe Select" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -33.94 USD Expenses:Food:Restaurant 33.94 USD 2015-04-23 * "Takahachi" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -57.88 USD Expenses:Food:Restaurant 57.88 USD 2015-04-23 * "Gimme! Coffee" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -8.34 USD Expenses:Food:Coffee 8.34 USD 2015-04-24 * "Laut" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -25.86 USD Expenses:Food:Restaurant 25.86 USD 2015-04-25 * "Uncle Boons" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -43.24 USD Expenses:Food:Restaurant 43.24 USD 2015-04-25 * "Cafe Select" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -37.04 USD Expenses:Food:Restaurant 37.04 USD 2015-04-26 * "Cafe Select" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -36.05 USD Expenses:Food:Restaurant 36.05 USD 2015-04-26 * "Takahachi" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -47.58 USD Expenses:Food:Restaurant 47.58 USD 2015-04-26 * "Gimme! Coffee" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -7.02 USD Expenses:Food:Coffee 7.02 USD 2015-04-27 * "Takahachi" "" #trip-new-york-2015 Liabilities:US:Chase:Slate -46.61 USD Expenses:Food:Restaurant 46.61 USD 2015-04-27 * "Consume vacation days" Assets:US:Hooli:Vacation -152 VACHR Expenses:Vacation 152 VACHR 2015-04-27 event "location" "New Metropolis" 2015-04-30 * "Kin Soy" "Eating out with Natasha" Liabilities:US:Chase:Slate -27.57 USD Expenses:Food:Restaurant 27.57 USD 2015-05-01 balance Liabilities:US:Chase:Slate -6925.08 USD 2015-05-02 * "Uncle Boons" "Eating out after work" Liabilities:US:Chase:Slate -31.94 USD Expenses:Food:Restaurant 31.94 USD 2015-05-03 * "Rose Flower" "Eating out with work buddies" Liabilities:US:Chase:Slate -46.15 USD Expenses:Food:Restaurant 46.15 USD 2015-05-04 * "Uncle Boons" "Eating out with Joe" Liabilities:US:Chase:Slate -28.57 USD Expenses:Food:Restaurant 28.57 USD 2015-05-07 * "China Garden" "Eating out with Julie" Liabilities:US:Chase:Slate -26.90 USD Expenses:Food:Restaurant 26.90 USD 2015-05-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 133.56 USD Assets:US:BofA:Checking -133.56 USD 2015-05-08 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -70.42 USD Expenses:Food:Groceries 70.42 USD 2015-05-10 * "Uncle Boons" "Eating out after work" Liabilities:US:Chase:Slate -28.14 USD Expenses:Food:Restaurant 28.14 USD 2015-05-12 * "Kin Soy" "Eating out with Julie" Liabilities:US:Chase:Slate -30.14 USD Expenses:Food:Restaurant 30.14 USD 2015-05-16 * "Kin Soy" "Eating out with Joe" Liabilities:US:Chase:Slate -68.65 USD Expenses:Food:Restaurant 68.65 USD 2015-05-16 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-05-20 * "China Garden" "Eating out with Julie" Liabilities:US:Chase:Slate -30.65 USD Expenses:Food:Restaurant 30.65 USD 2015-05-20 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -73.60 USD Expenses:Food:Groceries 73.60 USD 2015-05-23 * "Rose Flower" "Eating out with work buddies" Liabilities:US:Chase:Slate -39.87 USD Expenses:Food:Restaurant 39.87 USD 2015-05-26 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -108.92 USD Expenses:Food:Groceries 108.92 USD 2015-05-27 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -31.03 USD Expenses:Food:Restaurant 31.03 USD 2015-05-29 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -24.44 USD Expenses:Food:Restaurant 24.44 USD 2015-05-30 balance Liabilities:US:Chase:Slate -7550.94 USD 2015-06-01 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -66.32 USD Expenses:Food:Restaurant 66.32 USD 2015-06-02 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -73.30 USD Expenses:Food:Restaurant 73.30 USD 2015-06-06 * "Jewel of Morroco" "Eating out with Natasha" Liabilities:US:Chase:Slate -17.42 USD Expenses:Food:Restaurant 17.42 USD 2015-06-07 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -134.26 USD Expenses:Food:Groceries 134.26 USD 2015-06-07 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 917.16 USD Assets:US:BofA:Checking -917.16 USD 2015-06-08 * "Uncle Boons" "Eating out with work buddies" Liabilities:US:Chase:Slate -47.33 USD Expenses:Food:Restaurant 47.33 USD 2015-06-13 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -48.39 USD Expenses:Food:Restaurant 48.39 USD 2015-06-14 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-06-17 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -117.33 USD Expenses:Food:Groceries 117.33 USD 2015-06-18 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -31.04 USD Expenses:Food:Restaurant 31.04 USD 2015-06-20 balance Liabilities:US:Chase:Slate -7289.17 USD 2015-06-21 * "Uncle Boons" "Eating out after work" Liabilities:US:Chase:Slate -31.23 USD Expenses:Food:Restaurant 31.23 USD 2015-06-26 * "Uncle Boons" "Eating out with Julie" Liabilities:US:Chase:Slate -35.99 USD Expenses:Food:Restaurant 35.99 USD 2015-06-28 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -13.95 USD Expenses:Food:Restaurant 13.95 USD 2015-07-01 * "Jewel of Morroco" "Eating out with Julie" Liabilities:US:Chase:Slate -24.51 USD Expenses:Food:Restaurant 24.51 USD 2015-07-05 * "Cafe Modagor" "Eating out with work buddies" Liabilities:US:Chase:Slate -29.27 USD Expenses:Food:Restaurant 29.27 USD 2015-07-06 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -52.53 USD Expenses:Food:Groceries 52.53 USD 2015-07-08 * "Kin Soy" "Eating out " Liabilities:US:Chase:Slate -24.54 USD Expenses:Food:Restaurant 24.54 USD 2015-07-11 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 610.61 USD Assets:US:BofA:Checking -610.61 USD 2015-07-13 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -34.50 USD Expenses:Food:Restaurant 34.50 USD 2015-07-15 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-07-16 * "Kin Soy" "Eating out with Bill" Liabilities:US:Chase:Slate -37.49 USD Expenses:Food:Restaurant 37.49 USD 2015-07-17 balance Liabilities:US:Chase:Slate -7082.57 USD 2015-07-17 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -25.58 USD Expenses:Food:Restaurant 25.58 USD 2015-07-19 * "Jewel of Morroco" "Eating out after work" Liabilities:US:Chase:Slate -42.65 USD Expenses:Food:Restaurant 42.65 USD 2015-07-22 * "Goba Goba" "Eating out with work buddies" Liabilities:US:Chase:Slate -35.15 USD Expenses:Food:Restaurant 35.15 USD 2015-07-24 * "Cafe Modagor" "Eating out with Julie" Liabilities:US:Chase:Slate -71.59 USD Expenses:Food:Restaurant 71.59 USD 2015-07-24 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -93.04 USD Expenses:Food:Groceries 93.04 USD 2015-07-29 * "China Garden" "Eating out " Liabilities:US:Chase:Slate -54.54 USD Expenses:Food:Restaurant 54.54 USD 2015-07-30 * "Jewel of Morroco" "Eating out with Joe" Liabilities:US:Chase:Slate -32.69 USD Expenses:Food:Restaurant 32.69 USD 2015-08-04 * "Rose Flower" "Eating out after work" Liabilities:US:Chase:Slate -16.55 USD Expenses:Food:Restaurant 16.55 USD 2015-08-06 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -32.62 USD Expenses:Food:Restaurant 32.62 USD 2015-08-08 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 610.54 USD Assets:US:BofA:Checking -610.54 USD 2015-08-10 * "Chichipotle" "Eating out " Liabilities:US:Chase:Slate -48.64 USD Expenses:Food:Restaurant 48.64 USD 2015-08-11 * "Onion Market" "Buying groceries" Liabilities:US:Chase:Slate -71.75 USD Expenses:Food:Groceries 71.75 USD 2015-08-12 balance Liabilities:US:Chase:Slate -6996.83 USD 2015-08-14 * "China Garden" "Eating out with Joe" Liabilities:US:Chase:Slate -31.37 USD Expenses:Food:Restaurant 31.37 USD 2015-08-15 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-08-18 * "Rose Flower" "Eating out alone" Liabilities:US:Chase:Slate -28.00 USD Expenses:Food:Restaurant 28.00 USD 2015-08-22 * "Goba Goba" "Eating out with Bill" Liabilities:US:Chase:Slate -22.32 USD Expenses:Food:Restaurant 22.32 USD 2015-08-25 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -23.07 USD Expenses:Food:Restaurant 23.07 USD 2015-08-28 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -61.58 USD Expenses:Food:Groceries 61.58 USD 2015-08-30 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -36.67 USD Expenses:Food:Restaurant 36.67 USD 2015-08-31 * "Chichipotle" "Eating out with Julie" Liabilities:US:Chase:Slate -16.81 USD Expenses:Food:Restaurant 16.81 USD 2015-09-03 balance Liabilities:US:Chase:Slate -7336.65 USD 2015-09-05 * "Goba Goba" "Eating out with Julie" Liabilities:US:Chase:Slate -38.23 USD Expenses:Food:Restaurant 38.23 USD 2015-09-08 * "Goba Goba" "Eating out after work" Liabilities:US:Chase:Slate -19.68 USD Expenses:Food:Restaurant 19.68 USD 2015-09-08 * "Chase:Slate" "Paying off credit card" Liabilities:US:Chase:Slate 469.48 USD Assets:US:BofA:Checking -469.48 USD 2015-09-10 * "Uncle Boons" "Eating out with Natasha" Liabilities:US:Chase:Slate -38.24 USD Expenses:Food:Restaurant 38.24 USD 2015-09-11 * "Cafe Modagor" "Eating out " Liabilities:US:Chase:Slate -57.78 USD Expenses:Food:Restaurant 57.78 USD 2015-09-12 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD 2015-09-15 * "Cafe Modagor" "Eating out alone" Liabilities:US:Chase:Slate -57.22 USD Expenses:Food:Restaurant 57.22 USD 2015-09-16 * "Cafe Modagor" "Eating out with Joe" Liabilities:US:Chase:Slate -26.44 USD Expenses:Food:Restaurant 26.44 USD 2015-09-16 * "Farmer Fresh" "Buying groceries" Liabilities:US:Chase:Slate -85.00 USD Expenses:Food:Groceries 85.00 USD 2015-09-20 * "China Garden" "Eating out " Liabilities:US:Chase:Slate -29.40 USD Expenses:Food:Restaurant 29.40 USD 2015-09-25 balance Liabilities:US:Chase:Slate -7339.16 USD 2015-09-25 * "Uncle Boons" "Eating out with work buddies" Liabilities:US:Chase:Slate -32.42 USD Expenses:Food:Restaurant 32.42 USD 2015-09-29 * "Jewel of Morroco" "Eating out " Liabilities:US:Chase:Slate -30.43 USD Expenses:Food:Restaurant 30.43 USD 2015-09-30 * "Corner Deli" "Buying groceries" Liabilities:US:Chase:Slate -58.89 USD Expenses:Food:Groceries 58.89 USD 2015-10-01 * "China Garden" "Eating out with Natasha" Liabilities:US:Chase:Slate -39.01 USD Expenses:Food:Restaurant 39.01 USD 2015-10-03 * "Chichipotle" "Eating out with work buddies" Liabilities:US:Chase:Slate -36.93 USD Expenses:Food:Restaurant 36.93 USD 2015-10-08 * "Uncle Boons" "Eating out with work buddies" Liabilities:US:Chase:Slate -12.31 USD Expenses:Food:Restaurant 12.31 USD 2015-10-11 * "Metro Transport Authority" "Tram tickets" Liabilities:US:Chase:Slate -120.00 USD Expenses:Transport:Tram 120.00 USD * Taxable Investments 2008-01-01 open Assets:US:ETrade:Cash USD 2008-01-01 open Assets:US:ETrade:ITOT ITOT 2008-01-01 open Assets:US:ETrade:VEA VEA 2008-01-01 open Assets:US:ETrade:VHT VHT 2008-01-01 open Assets:US:ETrade:GLD GLD 2008-01-01 open Income:US:ETrade:Gains USD 2008-01-01 open Income:US:ETrade:Dividends USD 2008-08-28 * "Buy shares of ITOT" Assets:US:ETrade:Cash -1296.58 USD Assets:US:ETrade:ITOT 19 ITOT {67.77 USD, 2008-08-28} Expenses:Financial:Commissions 8.95 USD 2008-08-28 * "Buy shares of VEA" Assets:US:ETrade:Cash -1248.76 USD Assets:US:ETrade:VEA 13 VEA {95.37 USD, 2008-08-28} Expenses:Financial:Commissions 8.95 USD 2008-08-28 * "Buy shares of VHT" Assets:US:ETrade:Cash -1258.99 USD Assets:US:ETrade:VHT 22 VHT {56.82 USD, 2008-08-28} Expenses:Financial:Commissions 8.95 USD 2008-09-06 * "Sell shares of VEA" Assets:US:ETrade:VEA -13 VEA {95.37 USD, 2008-08-28} @ 95.58 USD Assets:US:ETrade:Cash 1233.59 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -2.73 USD 2008-09-10 * "Buy shares of VHT" Assets:US:ETrade:Cash -303.05 USD Assets:US:ETrade:VHT 5 VHT {58.82 USD, 2008-09-10} Expenses:Financial:Commissions 8.95 USD 2008-09-10 * "Buy shares of VEA" Assets:US:ETrade:Cash -295.69 USD Assets:US:ETrade:VEA 3 VEA {95.58 USD, 2008-09-10} Expenses:Financial:Commissions 8.95 USD 2008-09-10 * "Buy shares of GLD" Assets:US:ETrade:Cash -259.75 USD Assets:US:ETrade:GLD 4 GLD {62.70 USD, 2008-09-10} Expenses:Financial:Commissions 8.95 USD 2008-09-10 * "Buy shares of ITOT" Assets:US:ETrade:Cash -282.63 USD Assets:US:ETrade:ITOT 4 ITOT {68.42 USD, 2008-09-10} Expenses:Financial:Commissions 8.95 USD 2008-09-17 * "Dividends on portfolio" Assets:US:ETrade:Cash 19.53 USD Income:US:ETrade:Dividends -19.53 USD 2008-09-23 * "Sell shares of VEA" Assets:US:ETrade:VEA -3 VEA {95.58 USD, 2008-09-10} @ 95.62 USD Assets:US:ETrade:Cash 277.91 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -0.12 USD 2008-09-24 * "Sell shares of ITOT" Assets:US:ETrade:ITOT -4 ITOT {68.42 USD, 2008-09-10} @ 68.53 USD Assets:US:ETrade:Cash 265.17 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -0.44 USD 2008-11-09 * "Buy shares of GLD" Assets:US:ETrade:Cash -2556.85 USD Assets:US:ETrade:GLD 38 GLD {67.05 USD, 2008-11-09} Expenses:Financial:Commissions 8.95 USD 2008-11-09 * "Buy shares of VHT" Assets:US:ETrade:Cash -2580.47 USD Assets:US:ETrade:VHT 41 VHT {62.72 USD, 2008-11-09} Expenses:Financial:Commissions 8.95 USD 2008-12-17 * "Dividends on portfolio" Assets:US:ETrade:Cash 40.01 USD Income:US:ETrade:Dividends -40.01 USD 2009-01-29 * "Sell shares of VHT" Assets:US:ETrade:VHT -22 VHT {56.82 USD, 2008-08-28} @ 65.21 USD Assets:US:ETrade:Cash 1425.67 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -184.58 USD 2009-02-02 * "Buy shares of VHT" Assets:US:ETrade:Cash -403.33 USD Assets:US:ETrade:VHT 6 VHT {65.73 USD, 2009-02-02} Expenses:Financial:Commissions 8.95 USD 2009-02-02 * "Buy shares of ITOT" Assets:US:ETrade:Cash -363.30 USD Assets:US:ETrade:ITOT 5 ITOT {70.87 USD, 2009-02-02} Expenses:Financial:Commissions 8.95 USD 2009-02-02 * "Buy shares of GLD" Assets:US:ETrade:Cash -355.50 USD Assets:US:ETrade:GLD 5 GLD {69.31 USD, 2009-02-02} Expenses:Financial:Commissions 8.95 USD 2009-02-02 * "Buy shares of VEA" Assets:US:ETrade:Cash -406.39 USD Assets:US:ETrade:VEA 4 VEA {99.36 USD, 2009-02-02} Expenses:Financial:Commissions 8.95 USD 2009-03-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 45.98 USD Income:US:ETrade:Dividends -45.98 USD 2009-06-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 45.98 USD Income:US:ETrade:Dividends -45.98 USD 2009-07-24 * "Buy shares of GLD" Assets:US:ETrade:Cash -3457.39 USD Assets:US:ETrade:GLD 54 GLD {63.86 USD, 2009-07-24} Expenses:Financial:Commissions 8.95 USD 2009-09-15 * "Buy shares of VEA" Assets:US:ETrade:Cash -1148.75 USD Assets:US:ETrade:VEA 10 VEA {113.98 USD, 2009-09-15} Expenses:Financial:Commissions 8.95 USD 2009-09-15 * "Buy shares of GLD" Assets:US:ETrade:Cash -1191.98 USD Assets:US:ETrade:GLD 17 GLD {69.59 USD, 2009-09-15} Expenses:Financial:Commissions 8.95 USD 2009-09-15 * "Buy shares of VHT" Assets:US:ETrade:Cash -1199.47 USD Assets:US:ETrade:VHT 18 VHT {66.14 USD, 2009-09-15} Expenses:Financial:Commissions 8.95 USD 2009-09-15 * "Buy shares of ITOT" Assets:US:ETrade:Cash -1207.62 USD Assets:US:ETrade:ITOT 17 ITOT {70.51 USD, 2009-09-15} Expenses:Financial:Commissions 8.95 USD 2009-09-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 78.62 USD Income:US:ETrade:Dividends -78.62 USD 2009-11-09 * "Buy shares of VEA" Assets:US:ETrade:Cash -3703.03 USD Assets:US:ETrade:VEA 32 VEA {115.44 USD, 2009-11-09} Expenses:Financial:Commissions 8.95 USD 2009-12-17 * "Dividends on portfolio" Assets:US:ETrade:Cash 93.40 USD Income:US:ETrade:Dividends -93.40 USD 2010-03-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 93.40 USD Income:US:ETrade:Dividends -93.40 USD 2010-04-10 * "Sell shares of GLD" Assets:US:ETrade:GLD -38 GLD {67.05 USD, 2008-11-09} @ 63.42 USD Assets:US:ETrade:Cash 2401.01 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains 137.94 USD 2010-05-17 * "Buy shares of GLD" Assets:US:ETrade:Cash -1196.74 USD Assets:US:ETrade:GLD 17 GLD {69.87 USD, 2010-05-17} Expenses:Financial:Commissions 8.95 USD 2010-05-17 * "Buy shares of VEA" Assets:US:ETrade:Cash -1133.32 USD Assets:US:ETrade:VEA 9 VEA {124.93 USD, 2010-05-17} Expenses:Financial:Commissions 8.95 USD 2010-06-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 102.65 USD Income:US:ETrade:Dividends -102.65 USD 2010-07-25 * "Buy shares of ITOT" Assets:US:ETrade:Cash -1535.15 USD Assets:US:ETrade:ITOT 20 ITOT {76.31 USD, 2010-07-25} Expenses:Financial:Commissions 8.95 USD 2010-07-25 * "Buy shares of VEA" Assets:US:ETrade:Cash -1551.91 USD Assets:US:ETrade:VEA 12 VEA {128.58 USD, 2010-07-25} Expenses:Financial:Commissions 8.95 USD 2010-08-02 * "Sell shares of VHT" Assets:US:ETrade:VHT -41 VHT {62.72 USD, 2008-11-09} @ 75.96 USD Assets:US:ETrade:Cash 3105.41 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -542.84 USD 2010-08-22 * "Buy shares of VEA" Assets:US:ETrade:Cash -784.63 USD Assets:US:ETrade:VEA 6 VEA {129.28 USD, 2010-08-22} Expenses:Financial:Commissions 8.95 USD 2010-08-22 * "Buy shares of VHT" Assets:US:ETrade:Cash -770.15 USD Assets:US:ETrade:VHT 10 VHT {76.12 USD, 2010-08-22} Expenses:Financial:Commissions 8.95 USD 2010-08-22 * "Buy shares of ITOT" Assets:US:ETrade:Cash -766.45 USD Assets:US:ETrade:ITOT 10 ITOT {75.75 USD, 2010-08-22} Expenses:Financial:Commissions 8.95 USD 2010-08-22 * "Buy shares of GLD" Assets:US:ETrade:Cash -743.95 USD Assets:US:ETrade:GLD 10 GLD {73.50 USD, 2010-08-22} Expenses:Financial:Commissions 8.95 USD 2010-09-05 * "Buy shares of VEA" Assets:US:ETrade:Cash -1441.37 USD Assets:US:ETrade:VEA 11 VEA {130.22 USD, 2010-09-05} Expenses:Financial:Commissions 8.95 USD 2010-09-05 * "Buy shares of GLD" Assets:US:ETrade:Cash -1455.23 USD Assets:US:ETrade:GLD 19 GLD {76.12 USD, 2010-09-05} Expenses:Financial:Commissions 8.95 USD 2010-09-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 138.56 USD Income:US:ETrade:Dividends -138.56 USD 2010-10-08 * "Buy shares of GLD" Assets:US:ETrade:Cash -3771.17 USD Assets:US:ETrade:GLD 49 GLD {76.78 USD, 2010-10-08} Expenses:Financial:Commissions 8.95 USD 2010-12-06 * "Buy shares of ITOT" Assets:US:ETrade:Cash -641.11 USD Assets:US:ETrade:ITOT 8 ITOT {79.02 USD, 2010-12-06} Expenses:Financial:Commissions 8.95 USD 2010-12-06 * "Buy shares of VHT" Assets:US:ETrade:Cash -650.31 USD Assets:US:ETrade:VHT 8 VHT {80.17 USD, 2010-12-06} Expenses:Financial:Commissions 8.95 USD 2010-12-06 * "Buy shares of GLD" Assets:US:ETrade:Cash -704.74 USD Assets:US:ETrade:GLD 9 GLD {77.31 USD, 2010-12-06} Expenses:Financial:Commissions 8.95 USD 2010-12-06 * "Buy shares of VEA" Assets:US:ETrade:Cash -680.85 USD Assets:US:ETrade:VEA 5 VEA {134.38 USD, 2010-12-06} Expenses:Financial:Commissions 8.95 USD 2010-12-17 * "Dividends on portfolio" Assets:US:ETrade:Cash 164.17 USD Income:US:ETrade:Dividends -164.17 USD 2011-01-26 * "Sell shares of GLD" Assets:US:ETrade:GLD -49 GLD {76.78 USD, 2010-10-08} @ 75.75 USD Assets:US:ETrade:Cash 3702.80 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains 50.47 USD 2011-01-29 * "Buy shares of VHT" Assets:US:ETrade:Cash -3991.99 USD Assets:US:ETrade:VHT 48 VHT {82.98 USD, 2011-01-29} Expenses:Financial:Commissions 8.95 USD 2011-03-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 180.10 USD Income:US:ETrade:Dividends -180.10 USD 2011-06-12 * "Sell shares of GLD" Assets:US:ETrade:GLD -54 GLD {63.86 USD, 2009-07-24} @ 85.88 USD Assets:US:ETrade:Cash 4628.57 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -1189.08 USD 2011-06-18 * "Buy shares of ITOT" Assets:US:ETrade:Cash -4569.55 USD Assets:US:ETrade:ITOT 55 ITOT {82.92 USD, 2011-06-18} Expenses:Financial:Commissions 8.95 USD 2011-06-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 198.34 USD Income:US:ETrade:Dividends -198.34 USD 2011-06-26 * "Sell shares of ITOT" Assets:US:ETrade:ITOT -55 ITOT {82.92 USD, 2011-06-18} @ 82.25 USD Assets:US:ETrade:Cash 4514.80 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains 36.85 USD 2011-07-04 * "Buy shares of VHT" Assets:US:ETrade:Cash -1458.71 USD Assets:US:ETrade:VHT 17 VHT {85.28 USD, 2011-07-04} Expenses:Financial:Commissions 8.95 USD 2011-07-04 * "Buy shares of GLD" Assets:US:ETrade:Cash -1495.09 USD Assets:US:ETrade:GLD 17 GLD {87.42 USD, 2011-07-04} Expenses:Financial:Commissions 8.95 USD 2011-07-04 * "Buy shares of ITOT" Assets:US:ETrade:Cash -1501.69 USD Assets:US:ETrade:ITOT 18 ITOT {82.93 USD, 2011-07-04} Expenses:Financial:Commissions 8.95 USD 2011-09-01 * "Sell shares of VHT" Assets:US:ETrade:VHT -48 VHT {82.98 USD, 2011-01-29} @ 80.56 USD Assets:US:ETrade:Cash 3857.93 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains 116.16 USD 2011-09-11 * "Buy shares of VEA" Assets:US:ETrade:Cash -1687.22 USD Assets:US:ETrade:VEA 11 VEA {152.57 USD, 2011-09-11} Expenses:Financial:Commissions 8.95 USD 2011-09-11 * "Buy shares of GLD" Assets:US:ETrade:Cash -1698.43 USD Assets:US:ETrade:GLD 19 GLD {88.92 USD, 2011-09-11} Expenses:Financial:Commissions 8.95 USD 2011-09-11 * "Buy shares of VHT" Assets:US:ETrade:Cash -1707.22 USD Assets:US:ETrade:VHT 21 VHT {80.87 USD, 2011-09-11} Expenses:Financial:Commissions 8.95 USD 2011-09-11 * "Buy shares of ITOT" Assets:US:ETrade:Cash -1686.75 USD Assets:US:ETrade:ITOT 20 ITOT {83.89 USD, 2011-09-11} Expenses:Financial:Commissions 8.95 USD 2011-09-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 243.03 USD Income:US:ETrade:Dividends -243.03 USD 2011-09-29 * "Sell shares of GLD" Assets:US:ETrade:GLD -19 GLD {88.92 USD, 2011-09-11} @ 83.40 USD Assets:US:ETrade:Cash 1575.65 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains 104.88 USD 2011-09-30 * "Sell shares of VHT" Assets:US:ETrade:VHT -17 VHT {85.28 USD, 2011-07-04} @ 81.83 USD Assets:US:ETrade:Cash 1382.16 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains 58.65 USD 2011-10-14 * "Buy shares of VHT" Assets:US:ETrade:Cash -7180.03 USD Assets:US:ETrade:VHT 84 VHT {85.37 USD, 2011-10-14} Expenses:Financial:Commissions 8.95 USD 2011-11-19 * "Buy shares of GLD" Assets:US:ETrade:Cash -987.67 USD Assets:US:ETrade:GLD 12 GLD {81.56 USD, 2011-11-19} Expenses:Financial:Commissions 8.95 USD 2011-11-19 * "Buy shares of VHT" Assets:US:ETrade:Cash -967.27 USD Assets:US:ETrade:VHT 11 VHT {87.12 USD, 2011-11-19} Expenses:Financial:Commissions 8.95 USD 2011-11-19 * "Buy shares of VEA" Assets:US:ETrade:Cash -931.87 USD Assets:US:ETrade:VEA 6 VEA {153.82 USD, 2011-11-19} Expenses:Financial:Commissions 8.95 USD 2011-11-19 * "Buy shares of ITOT" Assets:US:ETrade:Cash -950.11 USD Assets:US:ETrade:ITOT 11 ITOT {85.56 USD, 2011-11-19} Expenses:Financial:Commissions 8.95 USD 2011-12-01 * "Sell shares of VEA" Assets:US:ETrade:VEA -32 VEA {115.44 USD, 2009-11-09} @ 156.42 USD Assets:US:ETrade:Cash 4996.49 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -1311.36 USD 2011-12-17 * "Dividends on portfolio" Assets:US:ETrade:Cash 286.92 USD Income:US:ETrade:Dividends -286.92 USD 2011-12-22 * "Buy shares of GLD" Assets:US:ETrade:Cash -1193.75 USD Assets:US:ETrade:GLD 16 GLD {74.05 USD, 2011-12-22} Expenses:Financial:Commissions 8.95 USD 2011-12-22 * "Buy shares of VHT" Assets:US:ETrade:Cash -1148.01 USD Assets:US:ETrade:VHT 13 VHT {87.62 USD, 2011-12-22} Expenses:Financial:Commissions 8.95 USD 2011-12-22 * "Buy shares of VEA" Assets:US:ETrade:Cash -1106.83 USD Assets:US:ETrade:VEA 7 VEA {156.84 USD, 2011-12-22} Expenses:Financial:Commissions 8.95 USD 2011-12-22 * "Buy shares of ITOT" Assets:US:ETrade:Cash -1145.02 USD Assets:US:ETrade:ITOT 13 ITOT {87.39 USD, 2011-12-22} Expenses:Financial:Commissions 8.95 USD 2012-01-23 * "Sell shares of ITOT" Assets:US:ETrade:ITOT -19 ITOT {67.77 USD, 2008-08-28} @ 92.52 USD Assets:US:ETrade:Cash 1748.93 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -470.25 USD 2012-02-13 * "Buy shares of ITOT" Assets:US:ETrade:Cash -2218.10 USD Assets:US:ETrade:ITOT 23 ITOT {96.05 USD, 2012-02-13} Expenses:Financial:Commissions 8.95 USD 2012-03-17 * "Dividends on portfolio" Assets:US:ETrade:Cash 313.99 USD Income:US:ETrade:Dividends -313.99 USD 2012-05-05 * "Sell shares of GLD" Assets:US:ETrade:GLD -17 GLD {87.42 USD, 2011-07-04} @ 68.89 USD Assets:US:ETrade:Cash 1162.18 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains 315.01 USD 2012-05-21 * "Buy shares of GLD" Assets:US:ETrade:Cash -298.63 USD Assets:US:ETrade:GLD 4 GLD {72.42 USD, 2012-05-21} Expenses:Financial:Commissions 8.95 USD 2012-05-21 * "Buy shares of VEA" Assets:US:ETrade:Cash -185.88 USD Assets:US:ETrade:VEA 1 VEA {176.93 USD, 2012-05-21} Expenses:Financial:Commissions 8.95 USD 2012-05-21 * "Buy shares of VHT" Assets:US:ETrade:Cash -280.84 USD Assets:US:ETrade:VHT 3 VHT {90.63 USD, 2012-05-21} Expenses:Financial:Commissions 8.95 USD 2012-05-21 * "Buy shares of ITOT" Assets:US:ETrade:Cash -301.00 USD Assets:US:ETrade:ITOT 3 ITOT {97.35 USD, 2012-05-21} Expenses:Financial:Commissions 8.95 USD 2012-06-17 * "Dividends on portfolio" Assets:US:ETrade:Cash 318.11 USD Income:US:ETrade:Dividends -318.11 USD 2012-07-26 * "Sell shares of VEA" Assets:US:ETrade:VEA -10 VEA {113.98 USD, 2009-09-15} @ 180.98 USD Assets:US:ETrade:Cash 1800.85 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -670.00 USD 2012-08-10 * "Buy shares of GLD" Assets:US:ETrade:Cash -2652.76 USD Assets:US:ETrade:GLD 39 GLD {67.79 USD, 2012-08-10} Expenses:Financial:Commissions 8.95 USD 2012-08-10 * "Buy shares of ITOT" Assets:US:ETrade:Cash -2640.10 USD Assets:US:ETrade:ITOT 27 ITOT {97.45 USD, 2012-08-10} Expenses:Financial:Commissions 8.95 USD 2012-09-17 * "Dividends on portfolio" Assets:US:ETrade:Cash 339.21 USD Income:US:ETrade:Dividends -339.21 USD 2012-09-21 * "Buy shares of GLD" Assets:US:ETrade:Cash -858.63 USD Assets:US:ETrade:GLD 13 GLD {65.36 USD, 2012-09-21} Expenses:Financial:Commissions 8.95 USD 2012-09-21 * "Buy shares of VHT" Assets:US:ETrade:Cash -822.55 USD Assets:US:ETrade:VHT 9 VHT {90.40 USD, 2012-09-21} Expenses:Financial:Commissions 8.95 USD 2012-09-21 * "Buy shares of VEA" Assets:US:ETrade:Cash -743.51 USD Assets:US:ETrade:VEA 4 VEA {183.64 USD, 2012-09-21} Expenses:Financial:Commissions 8.95 USD 2012-09-21 * "Buy shares of ITOT" Assets:US:ETrade:Cash -901.93 USD Assets:US:ETrade:ITOT 9 ITOT {99.22 USD, 2012-09-21} Expenses:Financial:Commissions 8.95 USD 2012-11-16 * "Buy shares of VHT" Assets:US:ETrade:Cash -3895.21 USD Assets:US:ETrade:VHT 42 VHT {92.53 USD, 2012-11-16} Expenses:Financial:Commissions 8.95 USD 2012-12-11 * "Sell shares of GLD" Assets:US:ETrade:GLD -12 GLD {81.56 USD, 2011-11-19} @ 70.65 USD Assets:US:ETrade:Cash 838.85 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains 130.92 USD 2012-12-17 * "Dividends on portfolio" Assets:US:ETrade:Cash 367.92 USD Income:US:ETrade:Dividends -367.92 USD 2013-02-01 * "Sell shares of VHT" Assets:US:ETrade:VHT -84 VHT {85.37 USD, 2011-10-14} @ 97.53 USD Assets:US:ETrade:Cash 8183.57 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -1021.44 USD 2013-02-10 * "Buy shares of GLD" Assets:US:ETrade:Cash -2182.31 USD Assets:US:ETrade:GLD 28 GLD {77.62 USD, 2013-02-10} Expenses:Financial:Commissions 8.95 USD 2013-02-10 * "Buy shares of ITOT" Assets:US:ETrade:Cash -2180.13 USD Assets:US:ETrade:ITOT 22 ITOT {98.69 USD, 2013-02-10} Expenses:Financial:Commissions 8.95 USD 2013-02-10 * "Buy shares of VEA" Assets:US:ETrade:Cash -2101.15 USD Assets:US:ETrade:VEA 11 VEA {190.20 USD, 2013-02-10} Expenses:Financial:Commissions 8.95 USD 2013-02-10 * "Buy shares of VHT" Assets:US:ETrade:Cash -2156.15 USD Assets:US:ETrade:VHT 22 VHT {97.60 USD, 2013-02-10} Expenses:Financial:Commissions 8.95 USD 2013-03-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 402.26 USD Income:US:ETrade:Dividends -402.26 USD 2013-05-01 * "Sell shares of GLD" Assets:US:ETrade:GLD -28 GLD {77.62 USD, 2013-02-10} @ 76.58 USD Assets:US:ETrade:Cash 2135.29 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains 29.12 USD 2013-05-04 * "Sell shares of VEA" Assets:US:ETrade:VEA -12 VEA {128.58 USD, 2010-07-25} @ 194.04 USD Assets:US:ETrade:Cash 2319.53 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -785.52 USD 2013-05-15 * "Buy shares of VEA" Assets:US:ETrade:Cash -1569.11 USD Assets:US:ETrade:VEA 8 VEA {195.02 USD, 2013-05-15} Expenses:Financial:Commissions 8.95 USD 2013-05-15 * "Buy shares of ITOT" Assets:US:ETrade:Cash -1525.01 USD Assets:US:ETrade:ITOT 14 ITOT {108.29 USD, 2013-05-15} Expenses:Financial:Commissions 8.95 USD 2013-05-15 * "Buy shares of GLD" Assets:US:ETrade:Cash -1540.55 USD Assets:US:ETrade:GLD 20 GLD {76.58 USD, 2013-05-15} Expenses:Financial:Commissions 8.95 USD 2013-06-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 420.69 USD Income:US:ETrade:Dividends -420.69 USD 2013-07-14 * "Sell shares of VHT" Assets:US:ETrade:VHT -22 VHT {97.60 USD, 2013-02-10} @ 92.28 USD Assets:US:ETrade:Cash 2021.21 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains 117.04 USD 2013-07-18 * "Buy shares of VEA" Assets:US:ETrade:Cash -2015.25 USD Assets:US:ETrade:VEA 10 VEA {200.63 USD, 2013-07-18} Expenses:Financial:Commissions 8.95 USD 2013-07-30 * "Sell shares of VEA" Assets:US:ETrade:VEA -10 VEA {200.63 USD, 2013-07-18} @ 199.93 USD Assets:US:ETrade:Cash 1990.35 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains 7.00 USD 2013-08-26 * "Buy shares of VEA" Assets:US:ETrade:Cash -2032.35 USD Assets:US:ETrade:VEA 10 VEA {202.34 USD, 2013-08-26} Expenses:Financial:Commissions 8.95 USD 2013-09-08 * "Buy shares of VEA" Assets:US:ETrade:Cash -1232.11 USD Assets:US:ETrade:VEA 6 VEA {203.86 USD, 2013-09-08} Expenses:Financial:Commissions 8.95 USD 2013-09-08 * "Buy shares of VHT" Assets:US:ETrade:Cash -1362.25 USD Assets:US:ETrade:VHT 15 VHT {90.22 USD, 2013-09-08} Expenses:Financial:Commissions 8.95 USD 2013-09-08 * "Buy shares of ITOT" Assets:US:ETrade:Cash -1357.99 USD Assets:US:ETrade:ITOT 12 ITOT {112.42 USD, 2013-09-08} Expenses:Financial:Commissions 8.95 USD 2013-09-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 452.51 USD Income:US:ETrade:Dividends -452.51 USD 2013-11-09 * "Buy shares of ITOT" Assets:US:ETrade:Cash -929.03 USD Assets:US:ETrade:ITOT 8 ITOT {115.01 USD, 2013-11-09} Expenses:Financial:Commissions 8.95 USD 2013-11-09 * "Buy shares of VHT" Assets:US:ETrade:Cash -939.25 USD Assets:US:ETrade:VHT 10 VHT {93.03 USD, 2013-11-09} Expenses:Financial:Commissions 8.95 USD 2013-11-09 * "Buy shares of GLD" Assets:US:ETrade:Cash -993.79 USD Assets:US:ETrade:GLD 12 GLD {82.07 USD, 2013-11-09} Expenses:Financial:Commissions 8.95 USD 2013-11-09 * "Buy shares of VEA" Assets:US:ETrade:Cash -823.55 USD Assets:US:ETrade:VEA 4 VEA {203.65 USD, 2013-11-09} Expenses:Financial:Commissions 8.95 USD 2013-11-19 * "Sell shares of GLD" Assets:US:ETrade:GLD -12 GLD {82.07 USD, 2013-11-09} @ 80.88 USD Assets:US:ETrade:Cash 961.61 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains 14.28 USD 2013-12-06 * "Buy shares of GLD" Assets:US:ETrade:Cash -667.19 USD Assets:US:ETrade:GLD 8 GLD {82.28 USD, 2013-12-06} Expenses:Financial:Commissions 8.95 USD 2013-12-06 * "Buy shares of VHT" Assets:US:ETrade:Cash -662.89 USD Assets:US:ETrade:VHT 7 VHT {93.42 USD, 2013-12-06} Expenses:Financial:Commissions 8.95 USD 2013-12-17 * "Dividends on portfolio" Assets:US:ETrade:Cash 472.36 USD Income:US:ETrade:Dividends -472.36 USD 2014-03-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 472.36 USD Income:US:ETrade:Dividends -472.36 USD 2014-04-11 * "Sell shares of ITOT" Assets:US:ETrade:ITOT -8 ITOT {115.01 USD, 2013-11-09} @ 116.49 USD Assets:US:ETrade:Cash 922.97 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -11.84 USD 2014-04-30 * "Buy shares of VHT" Assets:US:ETrade:Cash -110.48 USD Assets:US:ETrade:VHT 1 VHT {101.53 USD, 2014-04-30} Expenses:Financial:Commissions 8.95 USD 2014-04-30 * "Buy shares of GLD" Assets:US:ETrade:Cash -111.77 USD Assets:US:ETrade:GLD 1 GLD {102.82 USD, 2014-04-30} Expenses:Financial:Commissions 8.95 USD 2014-04-30 * "Buy shares of ITOT" Assets:US:ETrade:Cash -130.33 USD Assets:US:ETrade:ITOT 1 ITOT {121.38 USD, 2014-04-30} Expenses:Financial:Commissions 8.95 USD 2014-06-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 473.66 USD Income:US:ETrade:Dividends -473.66 USD 2014-07-01 * "Sell shares of ITOT" Assets:US:ETrade:ITOT -1 ITOT {121.38 USD, 2014-04-30} @ 113.70 USD Assets:US:ETrade:Cash 104.75 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains 7.68 USD 2014-08-06 * "Sell shares of VHT" Assets:US:ETrade:VHT -1 VHT {101.53 USD, 2014-04-30} @ 107.96 USD Assets:US:ETrade:Cash 99.01 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -6.43 USD 2014-08-07 * "Sell shares of GLD" Assets:US:ETrade:GLD -39 GLD {67.79 USD, 2012-08-10} @ 109.36 USD Assets:US:ETrade:Cash 4256.09 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -1621.23 USD 2014-08-17 * "Buy shares of VEA" Assets:US:ETrade:Cash -2337.05 USD Assets:US:ETrade:VEA 10 VEA {232.81 USD, 2014-08-17} Expenses:Financial:Commissions 8.95 USD 2014-08-17 * "Buy shares of VHT" Assets:US:ETrade:Cash -2372.50 USD Assets:US:ETrade:VHT 21 VHT {112.55 USD, 2014-08-17} Expenses:Financial:Commissions 8.95 USD 2014-08-17 * "Buy shares of GLD" Assets:US:ETrade:Cash -2319.37 USD Assets:US:ETrade:GLD 21 GLD {110.02 USD, 2014-08-17} Expenses:Financial:Commissions 8.95 USD 2014-08-17 * "Buy shares of ITOT" Assets:US:ETrade:Cash -2392.66 USD Assets:US:ETrade:ITOT 21 ITOT {113.51 USD, 2014-08-17} Expenses:Financial:Commissions 8.95 USD 2014-09-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 511.20 USD Income:US:ETrade:Dividends -511.20 USD 2014-10-14 * "Buy shares of VEA" Assets:US:ETrade:Cash -3725.43 USD Assets:US:ETrade:VEA 16 VEA {232.28 USD, 2014-10-14} Expenses:Financial:Commissions 8.95 USD 2014-10-31 * "Sell shares of VHT" Assets:US:ETrade:VHT -42 VHT {92.53 USD, 2012-11-16} @ 122.58 USD Assets:US:ETrade:Cash 5139.41 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -1262.10 USD 2014-11-07 * "Buy shares of ITOT" Assets:US:ETrade:Cash -1219.45 USD Assets:US:ETrade:ITOT 10 ITOT {121.05 USD, 2014-11-07} Expenses:Financial:Commissions 8.95 USD 2014-11-07 * "Buy shares of VHT" Assets:US:ETrade:Cash -1257.85 USD Assets:US:ETrade:VHT 10 VHT {124.89 USD, 2014-11-07} Expenses:Financial:Commissions 8.95 USD 2014-11-07 * "Buy shares of GLD" Assets:US:ETrade:Cash -1298.81 USD Assets:US:ETrade:GLD 11 GLD {117.26 USD, 2014-11-07} Expenses:Financial:Commissions 8.95 USD 2014-11-07 * "Buy shares of VEA" Assets:US:ETrade:Cash -1190.50 USD Assets:US:ETrade:VEA 5 VEA {236.31 USD, 2014-11-07} Expenses:Financial:Commissions 8.95 USD 2014-11-18 * "Buy shares of ITOT" Assets:US:ETrade:Cash -4197.41 USD Assets:US:ETrade:ITOT 34 ITOT {123.19 USD, 2014-11-18} Expenses:Financial:Commissions 8.95 USD 2014-12-03 * "Sell shares of VEA" Assets:US:ETrade:VEA -11 VEA {130.22 USD, 2010-09-05} @ 231.35 USD Assets:US:ETrade:Cash 2535.90 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -1112.43 USD 2014-12-11 * "Buy shares of VEA" Assets:US:ETrade:Cash -703.84 USD Assets:US:ETrade:VEA 3 VEA {231.63 USD, 2014-12-11} Expenses:Financial:Commissions 8.95 USD 2014-12-11 * "Buy shares of VHT" Assets:US:ETrade:Cash -762.01 USD Assets:US:ETrade:VHT 6 VHT {125.51 USD, 2014-12-11} Expenses:Financial:Commissions 8.95 USD 2014-12-11 * "Buy shares of GLD" Assets:US:ETrade:Cash -763.87 USD Assets:US:ETrade:GLD 6 GLD {125.82 USD, 2014-12-11} Expenses:Financial:Commissions 8.95 USD 2014-12-17 * "Dividends on portfolio" Assets:US:ETrade:Cash 571.36 USD Income:US:ETrade:Dividends -571.36 USD 2014-12-24 * "Sell shares of VHT" Assets:US:ETrade:VHT -18 VHT {66.14 USD, 2009-09-15} @ 128.14 USD Assets:US:ETrade:Cash 2297.57 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -1116.00 USD 2015-01-05 * "Buy shares of VHT" Assets:US:ETrade:Cash -781.87 USD Assets:US:ETrade:VHT 6 VHT {128.82 USD, 2015-01-05} Expenses:Financial:Commissions 8.95 USD 2015-01-05 * "Buy shares of VEA" Assets:US:ETrade:Cash -710.47 USD Assets:US:ETrade:VEA 3 VEA {233.84 USD, 2015-01-05} Expenses:Financial:Commissions 8.95 USD 2015-01-05 * "Buy shares of ITOT" Assets:US:ETrade:Cash -723.31 USD Assets:US:ETrade:ITOT 6 ITOT {119.06 USD, 2015-01-05} Expenses:Financial:Commissions 8.95 USD 2015-02-06 * "Sell shares of VHT" Assets:US:ETrade:VHT -21 VHT {80.87 USD, 2011-09-11} @ 131.82 USD Assets:US:ETrade:Cash 2759.27 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -1069.95 USD 2015-02-12 * "Buy shares of VEA" Assets:US:ETrade:Cash -720.55 USD Assets:US:ETrade:VEA 3 VEA {237.20 USD, 2015-02-12} Expenses:Financial:Commissions 8.95 USD 2015-02-12 * "Buy shares of VHT" Assets:US:ETrade:Cash -799.87 USD Assets:US:ETrade:VHT 6 VHT {131.82 USD, 2015-02-12} Expenses:Financial:Commissions 8.95 USD 2015-02-12 * "Buy shares of GLD" Assets:US:ETrade:Cash -759.19 USD Assets:US:ETrade:GLD 6 GLD {125.04 USD, 2015-02-12} Expenses:Financial:Commissions 8.95 USD 2015-02-12 * "Buy shares of ITOT" Assets:US:ETrade:Cash -737.41 USD Assets:US:ETrade:ITOT 6 ITOT {121.41 USD, 2015-02-12} Expenses:Financial:Commissions 8.95 USD 2015-03-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 592.04 USD Income:US:ETrade:Dividends -592.04 USD 2015-03-23 * "Sell shares of VHT" Assets:US:ETrade:VHT -6 VHT {131.82 USD, 2015-02-12} @ 127.87 USD Assets:US:ETrade:Cash 758.27 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains 23.70 USD 2015-06-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 592.04 USD Income:US:ETrade:Dividends -592.04 USD 2015-07-24 * "Sell shares of ITOT" Assets:US:ETrade:ITOT -20 ITOT {76.31 USD, 2010-07-25} @ 136.19 USD Assets:US:ETrade:Cash 2714.85 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -1197.60 USD 2015-07-29 * "Buy shares of VEA" Assets:US:ETrade:Cash -1759.09 USD Assets:US:ETrade:VEA 7 VEA {250.02 USD, 2015-07-29} Expenses:Financial:Commissions 8.95 USD 2015-07-29 * "Buy shares of VHT" Assets:US:ETrade:Cash -1786.44 USD Assets:US:ETrade:VHT 13 VHT {136.73 USD, 2015-07-29} Expenses:Financial:Commissions 8.95 USD 2015-09-15 * "Buy shares of VHT" Assets:US:ETrade:Cash -1889.15 USD Assets:US:ETrade:VHT 14 VHT {134.30 USD, 2015-09-15} Expenses:Financial:Commissions 8.95 USD 2015-09-15 * "Buy shares of VEA" Assets:US:ETrade:Cash -1761.96 USD Assets:US:ETrade:VEA 7 VEA {250.43 USD, 2015-09-15} Expenses:Financial:Commissions 8.95 USD 2015-09-18 * "Dividends on portfolio" Assets:US:ETrade:Cash 620.68 USD Income:US:ETrade:Dividends -620.68 USD 2015-09-22 * "Sell shares of GLD" Assets:US:ETrade:GLD -20 GLD {76.58 USD, 2013-05-15} @ 139.54 USD Assets:US:ETrade:Cash 2781.85 USD Expenses:Financial:Commissions 8.95 USD Income:US:ETrade:Gains -1259.20 USD 2015-10-03 * "Buy shares of ITOT" Assets:US:ETrade:Cash -8100.57 USD Assets:US:ETrade:ITOT 62 ITOT {130.51 USD, 2015-10-03} Expenses:Financial:Commissions 8.95 USD * Vanguard Investments 2008-01-01 open Assets:US:Vanguard:VBMPX VBMPX number: "882882" 2008-01-01 open Assets:US:Vanguard:RGAGX RGAGX number: "882882" 2008-01-01 open Assets:US:Vanguard USD address: "P.O. Box 1110, Valley Forge, PA 19482-1110" institution: "Vanguard Group" phone: "+1.800.523.1188" 2008-01-01 open Income:US:Hooli:Match401k USD 2008-01-01 open Assets:US:Vanguard:Cash USD number: "882882" 2008-01-04 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2008-01-07 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.419 VBMPX {108.61 USD, 2008-01-07} Assets:US:Vanguard:Cash -479.95 USD 2008-01-07 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.616 RGAGX {155.99 USD, 2008-01-07} Assets:US:Vanguard:Cash -720.05 USD 2008-01-07 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.210 VBMPX {108.61 USD, 2008-01-07} Assets:US:Vanguard:Cash -240.03 USD 2008-01-07 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.308 RGAGX {155.99 USD, 2008-01-07} Assets:US:Vanguard:Cash -360.02 USD 2008-01-18 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2008-01-21 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.441 VBMPX {108.07 USD, 2008-01-21} Assets:US:Vanguard:Cash -479.94 USD 2008-01-21 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.498 RGAGX {160.08 USD, 2008-01-21} Assets:US:Vanguard:Cash -720.04 USD 2008-01-21 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.221 VBMPX {108.07 USD, 2008-01-21} Assets:US:Vanguard:Cash -240.02 USD 2008-01-21 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.249 RGAGX {160.08 USD, 2008-01-21} Assets:US:Vanguard:Cash -360.02 USD 2008-02-01 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2008-02-04 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.261 VBMPX {112.63 USD, 2008-02-04} Assets:US:Vanguard:Cash -479.92 USD 2008-02-04 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.433 RGAGX {162.42 USD, 2008-02-04} Assets:US:Vanguard:Cash -720.01 USD 2008-02-04 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.131 VBMPX {112.63 USD, 2008-02-04} Assets:US:Vanguard:Cash -240.01 USD 2008-02-04 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.216 RGAGX {162.42 USD, 2008-02-04} Assets:US:Vanguard:Cash -359.92 USD 2008-02-15 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2008-02-18 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.019 VBMPX {119.43 USD, 2008-02-18} Assets:US:Vanguard:Cash -479.99 USD 2008-02-18 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.370 RGAGX {164.78 USD, 2008-02-18} Assets:US:Vanguard:Cash -720.09 USD 2008-02-18 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.010 VBMPX {119.43 USD, 2008-02-18} Assets:US:Vanguard:Cash -240.05 USD 2008-02-18 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.185 RGAGX {164.78 USD, 2008-02-18} Assets:US:Vanguard:Cash -360.04 USD 2008-02-29 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2008-03-03 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.980 VBMPX {120.58 USD, 2008-03-03} Assets:US:Vanguard:Cash -479.91 USD 2008-03-03 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.545 RGAGX {158.41 USD, 2008-03-03} Assets:US:Vanguard:Cash -719.97 USD 2008-03-03 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.990 VBMPX {120.58 USD, 2008-03-03} Assets:US:Vanguard:Cash -239.95 USD 2008-03-03 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.273 RGAGX {158.41 USD, 2008-03-03} Assets:US:Vanguard:Cash -360.07 USD 2008-03-14 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2008-03-17 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.983 VBMPX {120.51 USD, 2008-03-17} Assets:US:Vanguard:Cash -479.99 USD 2008-03-17 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.582 RGAGX {157.13 USD, 2008-03-17} Assets:US:Vanguard:Cash -719.97 USD 2008-03-17 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.992 VBMPX {120.51 USD, 2008-03-17} Assets:US:Vanguard:Cash -240.06 USD 2008-03-17 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.291 RGAGX {157.13 USD, 2008-03-17} Assets:US:Vanguard:Cash -359.98 USD 2008-03-28 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2008-03-31 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 4.025 VBMPX {119.26 USD, 2008-03-31} Assets:US:Vanguard:Cash -480.02 USD 2008-03-31 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.511 RGAGX {159.60 USD, 2008-03-31} Assets:US:Vanguard:Cash -719.96 USD 2008-03-31 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.012 VBMPX {119.26 USD, 2008-03-31} Assets:US:Vanguard:Cash -239.95 USD 2008-03-31 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.256 RGAGX {159.60 USD, 2008-03-31} Assets:US:Vanguard:Cash -360.06 USD 2008-04-11 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2008-04-14 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.800 VBMPX {126.33 USD, 2008-04-14} Assets:US:Vanguard:Cash -480.05 USD 2008-04-14 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.416 RGAGX {163.04 USD, 2008-04-14} Assets:US:Vanguard:Cash -719.98 USD 2008-04-14 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.900 VBMPX {126.33 USD, 2008-04-14} Assets:US:Vanguard:Cash -240.03 USD 2008-04-14 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.208 RGAGX {163.04 USD, 2008-04-14} Assets:US:Vanguard:Cash -359.99 USD 2008-04-25 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2008-04-28 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.941 VBMPX {121.80 USD, 2008-04-28} Assets:US:Vanguard:Cash -480.01 USD 2008-04-28 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.360 RGAGX {165.12 USD, 2008-04-28} Assets:US:Vanguard:Cash -719.92 USD 2008-04-28 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.971 VBMPX {121.80 USD, 2008-04-28} Assets:US:Vanguard:Cash -240.07 USD 2008-04-28 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.180 RGAGX {165.12 USD, 2008-04-28} Assets:US:Vanguard:Cash -359.96 USD 2008-05-09 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2008-05-12 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.843 VBMPX {124.90 USD, 2008-05-12} Assets:US:Vanguard:Cash -479.99 USD 2008-05-12 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.313 RGAGX {166.95 USD, 2008-05-12} Assets:US:Vanguard:Cash -720.06 USD 2008-05-12 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.921 VBMPX {124.90 USD, 2008-05-12} Assets:US:Vanguard:Cash -239.93 USD 2008-05-12 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.156 RGAGX {166.95 USD, 2008-05-12} Assets:US:Vanguard:Cash -359.94 USD 2008-05-23 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2008-05-26 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.944 VBMPX {121.72 USD, 2008-05-26} Assets:US:Vanguard:Cash -480.06 USD 2008-05-26 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.232 RGAGX {170.16 USD, 2008-05-26} Assets:US:Vanguard:Cash -720.12 USD 2008-05-26 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.971 VBMPX {121.72 USD, 2008-05-26} Assets:US:Vanguard:Cash -239.91 USD 2008-05-26 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.115 RGAGX {170.16 USD, 2008-05-26} Assets:US:Vanguard:Cash -359.89 USD 2008-06-06 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2008-06-09 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.921 VBMPX {122.42 USD, 2008-06-09} Assets:US:Vanguard:Cash -480.01 USD 2008-06-09 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 4.126 RGAGX {174.50 USD, 2008-06-09} Assets:US:Vanguard:Cash -719.99 USD 2008-06-09 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.961 VBMPX {122.42 USD, 2008-06-09} Assets:US:Vanguard:Cash -240.07 USD 2008-06-09 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.063 RGAGX {174.50 USD, 2008-06-09} Assets:US:Vanguard:Cash -359.99 USD 2008-06-20 * "Employer match for contribution" Assets:US:Vanguard:Cash 550.00 USD Income:US:Hooli:Match401k -550.00 USD 2008-06-23 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.601 VBMPX {122.18 USD, 2008-06-23} Assets:US:Vanguard:Cash -439.97 USD 2008-06-23 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 3.805 RGAGX {173.44 USD, 2008-06-23} Assets:US:Vanguard:Cash -659.94 USD 2008-06-23 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.801 VBMPX {122.18 USD, 2008-06-23} Assets:US:Vanguard:Cash -220.05 USD 2008-06-23 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.903 RGAGX {173.44 USD, 2008-06-23} Assets:US:Vanguard:Cash -330.06 USD 2009-01-02 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2009-01-05 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.878 VBMPX {123.78 USD, 2009-01-05} Assets:US:Vanguard:Cash -480.02 USD 2009-01-05 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 3.857 RGAGX {186.67 USD, 2009-01-05} Assets:US:Vanguard:Cash -719.99 USD 2009-01-05 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.939 VBMPX {123.78 USD, 2009-01-05} Assets:US:Vanguard:Cash -240.01 USD 2009-01-05 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.929 RGAGX {186.67 USD, 2009-01-05} Assets:US:Vanguard:Cash -360.09 USD 2009-01-16 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2009-01-19 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.848 VBMPX {124.73 USD, 2009-01-19} Assets:US:Vanguard:Cash -479.96 USD 2009-01-19 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 3.870 RGAGX {186.03 USD, 2009-01-19} Assets:US:Vanguard:Cash -719.94 USD 2009-01-19 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.924 VBMPX {124.73 USD, 2009-01-19} Assets:US:Vanguard:Cash -239.98 USD 2009-01-19 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.935 RGAGX {186.03 USD, 2009-01-19} Assets:US:Vanguard:Cash -359.97 USD 2009-01-30 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2009-02-02 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.890 VBMPX {123.40 USD, 2009-02-02} Assets:US:Vanguard:Cash -480.03 USD 2009-02-02 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 3.779 RGAGX {190.55 USD, 2009-02-02} Assets:US:Vanguard:Cash -720.09 USD 2009-02-02 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.945 VBMPX {123.40 USD, 2009-02-02} Assets:US:Vanguard:Cash -240.01 USD 2009-02-02 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.889 RGAGX {190.55 USD, 2009-02-02} Assets:US:Vanguard:Cash -359.95 USD 2009-02-13 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2009-02-16 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.842 VBMPX {124.94 USD, 2009-02-16} Assets:US:Vanguard:Cash -480.02 USD 2009-02-16 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 3.696 RGAGX {194.81 USD, 2009-02-16} Assets:US:Vanguard:Cash -720.02 USD 2009-02-16 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.921 VBMPX {124.94 USD, 2009-02-16} Assets:US:Vanguard:Cash -240.01 USD 2009-02-16 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.848 RGAGX {194.81 USD, 2009-02-16} Assets:US:Vanguard:Cash -360.01 USD 2009-02-27 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2009-03-02 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.823 VBMPX {125.55 USD, 2009-03-02} Assets:US:Vanguard:Cash -479.98 USD 2009-03-02 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 3.620 RGAGX {198.86 USD, 2009-03-02} Assets:US:Vanguard:Cash -719.87 USD 2009-03-02 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.912 VBMPX {125.55 USD, 2009-03-02} Assets:US:Vanguard:Cash -240.05 USD 2009-03-02 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.811 RGAGX {198.86 USD, 2009-03-02} Assets:US:Vanguard:Cash -360.14 USD 2009-03-13 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2009-03-16 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.749 VBMPX {128.01 USD, 2009-03-16} Assets:US:Vanguard:Cash -479.91 USD 2009-03-16 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 3.525 RGAGX {204.25 USD, 2009-03-16} Assets:US:Vanguard:Cash -719.98 USD 2009-03-16 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.875 VBMPX {128.01 USD, 2009-03-16} Assets:US:Vanguard:Cash -240.02 USD 2009-03-16 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.763 RGAGX {204.25 USD, 2009-03-16} Assets:US:Vanguard:Cash -360.09 USD 2009-03-27 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2009-03-30 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.734 VBMPX {128.54 USD, 2009-03-30} Assets:US:Vanguard:Cash -479.97 USD 2009-03-30 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 3.529 RGAGX {203.99 USD, 2009-03-30} Assets:US:Vanguard:Cash -719.88 USD 2009-03-30 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.867 VBMPX {128.54 USD, 2009-03-30} Assets:US:Vanguard:Cash -239.98 USD 2009-03-30 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.765 RGAGX {203.99 USD, 2009-03-30} Assets:US:Vanguard:Cash -360.04 USD 2009-04-10 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2009-04-13 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.689 VBMPX {130.11 USD, 2009-04-13} Assets:US:Vanguard:Cash -479.98 USD 2009-04-13 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 3.517 RGAGX {204.75 USD, 2009-04-13} Assets:US:Vanguard:Cash -720.11 USD 2009-04-13 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.844 VBMPX {130.11 USD, 2009-04-13} Assets:US:Vanguard:Cash -239.92 USD 2009-04-13 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.758 RGAGX {204.75 USD, 2009-04-13} Assets:US:Vanguard:Cash -359.95 USD 2009-04-24 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2009-04-27 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.750 VBMPX {128.01 USD, 2009-04-27} Assets:US:Vanguard:Cash -480.04 USD 2009-04-27 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 3.492 RGAGX {206.18 USD, 2009-04-27} Assets:US:Vanguard:Cash -719.98 USD 2009-04-27 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.875 VBMPX {128.01 USD, 2009-04-27} Assets:US:Vanguard:Cash -240.02 USD 2009-04-27 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.746 RGAGX {206.18 USD, 2009-04-27} Assets:US:Vanguard:Cash -359.99 USD 2009-05-08 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2009-05-11 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.732 VBMPX {128.62 USD, 2009-05-11} Assets:US:Vanguard:Cash -480.01 USD 2009-05-11 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 3.421 RGAGX {210.49 USD, 2009-05-11} Assets:US:Vanguard:Cash -720.09 USD 2009-05-11 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.866 VBMPX {128.62 USD, 2009-05-11} Assets:US:Vanguard:Cash -240.00 USD 2009-05-11 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.710 RGAGX {210.49 USD, 2009-05-11} Assets:US:Vanguard:Cash -359.94 USD 2009-05-22 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2009-05-25 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.574 VBMPX {134.30 USD, 2009-05-25} Assets:US:Vanguard:Cash -479.99 USD 2009-05-25 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 3.484 RGAGX {206.65 USD, 2009-05-25} Assets:US:Vanguard:Cash -719.97 USD 2009-05-25 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.787 VBMPX {134.30 USD, 2009-05-25} Assets:US:Vanguard:Cash -239.99 USD 2009-05-25 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.742 RGAGX {206.65 USD, 2009-05-25} Assets:US:Vanguard:Cash -359.98 USD 2009-06-05 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2009-06-08 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.485 VBMPX {137.73 USD, 2009-06-08} Assets:US:Vanguard:Cash -479.99 USD 2009-06-08 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 3.431 RGAGX {209.87 USD, 2009-06-08} Assets:US:Vanguard:Cash -720.06 USD 2009-06-08 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.743 VBMPX {137.73 USD, 2009-06-08} Assets:US:Vanguard:Cash -240.06 USD 2009-06-08 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.715 RGAGX {209.87 USD, 2009-06-08} Assets:US:Vanguard:Cash -359.93 USD 2009-06-19 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2009-06-22 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.480 VBMPX {137.94 USD, 2009-06-22} Assets:US:Vanguard:Cash -480.03 USD 2009-06-22 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 3.361 RGAGX {214.22 USD, 2009-06-22} Assets:US:Vanguard:Cash -719.99 USD 2009-06-22 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.740 VBMPX {137.94 USD, 2009-06-22} Assets:US:Vanguard:Cash -240.02 USD 2009-06-22 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.680 RGAGX {214.22 USD, 2009-06-22} Assets:US:Vanguard:Cash -359.89 USD 2009-07-03 * "Employer match for contribution" Assets:US:Vanguard:Cash 450.00 USD Income:US:Hooli:Match401k -450.00 USD 2009-07-06 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.535 VBMPX {142.05 USD, 2009-07-06} Assets:US:Vanguard:Cash -360.10 USD 2009-07-06 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.471 RGAGX {218.53 USD, 2009-07-06} Assets:US:Vanguard:Cash -539.99 USD 2009-07-06 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.267 VBMPX {142.05 USD, 2009-07-06} Assets:US:Vanguard:Cash -179.98 USD 2009-07-06 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.236 RGAGX {218.53 USD, 2009-07-06} Assets:US:Vanguard:Cash -270.10 USD 2010-01-15 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2010-01-18 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.315 VBMPX {144.77 USD, 2010-01-18} Assets:US:Vanguard:Cash -479.91 USD 2010-01-18 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.757 RGAGX {261.13 USD, 2010-01-18} Assets:US:Vanguard:Cash -719.94 USD 2010-01-18 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.658 VBMPX {144.77 USD, 2010-01-18} Assets:US:Vanguard:Cash -240.03 USD 2010-01-18 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.379 RGAGX {261.13 USD, 2010-01-18} Assets:US:Vanguard:Cash -360.10 USD 2010-01-29 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2010-02-01 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.383 VBMPX {141.86 USD, 2010-02-01} Assets:US:Vanguard:Cash -479.91 USD 2010-02-01 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.692 RGAGX {267.45 USD, 2010-02-01} Assets:US:Vanguard:Cash -719.98 USD 2010-02-01 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.692 VBMPX {141.86 USD, 2010-02-01} Assets:US:Vanguard:Cash -240.03 USD 2010-02-01 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.346 RGAGX {267.45 USD, 2010-02-01} Assets:US:Vanguard:Cash -359.99 USD 2010-02-12 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2010-02-15 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.343 VBMPX {143.58 USD, 2010-02-15} Assets:US:Vanguard:Cash -479.99 USD 2010-02-15 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.697 RGAGX {266.94 USD, 2010-02-15} Assets:US:Vanguard:Cash -719.94 USD 2010-02-15 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.672 VBMPX {143.58 USD, 2010-02-15} Assets:US:Vanguard:Cash -240.07 USD 2010-02-15 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.349 RGAGX {266.94 USD, 2010-02-15} Assets:US:Vanguard:Cash -360.10 USD 2010-02-26 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2010-03-01 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.346 VBMPX {143.43 USD, 2010-03-01} Assets:US:Vanguard:Cash -479.92 USD 2010-03-01 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.764 RGAGX {260.52 USD, 2010-03-01} Assets:US:Vanguard:Cash -720.08 USD 2010-03-01 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.673 VBMPX {143.43 USD, 2010-03-01} Assets:US:Vanguard:Cash -239.96 USD 2010-03-01 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.382 RGAGX {260.52 USD, 2010-03-01} Assets:US:Vanguard:Cash -360.04 USD 2010-03-12 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2010-03-15 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.297 VBMPX {145.57 USD, 2010-03-15} Assets:US:Vanguard:Cash -479.94 USD 2010-03-15 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.799 RGAGX {257.24 USD, 2010-03-15} Assets:US:Vanguard:Cash -720.01 USD 2010-03-15 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.649 VBMPX {145.57 USD, 2010-03-15} Assets:US:Vanguard:Cash -240.04 USD 2010-03-15 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.399 RGAGX {257.24 USD, 2010-03-15} Assets:US:Vanguard:Cash -359.88 USD 2010-03-26 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2010-03-29 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.179 VBMPX {151.01 USD, 2010-03-29} Assets:US:Vanguard:Cash -480.06 USD 2010-03-29 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.785 RGAGX {258.51 USD, 2010-03-29} Assets:US:Vanguard:Cash -719.95 USD 2010-03-29 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.589 VBMPX {151.01 USD, 2010-03-29} Assets:US:Vanguard:Cash -239.95 USD 2010-03-29 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.393 RGAGX {258.51 USD, 2010-03-29} Assets:US:Vanguard:Cash -360.10 USD 2010-04-09 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2010-04-12 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.186 VBMPX {150.67 USD, 2010-04-12} Assets:US:Vanguard:Cash -480.03 USD 2010-04-12 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.704 RGAGX {266.26 USD, 2010-04-12} Assets:US:Vanguard:Cash -719.97 USD 2010-04-12 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.593 VBMPX {150.67 USD, 2010-04-12} Assets:US:Vanguard:Cash -240.02 USD 2010-04-12 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.352 RGAGX {266.26 USD, 2010-04-12} Assets:US:Vanguard:Cash -359.98 USD 2010-04-23 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2010-04-26 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.197 VBMPX {150.15 USD, 2010-04-26} Assets:US:Vanguard:Cash -480.03 USD 2010-04-26 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.645 RGAGX {272.20 USD, 2010-04-26} Assets:US:Vanguard:Cash -719.97 USD 2010-04-26 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.598 VBMPX {150.15 USD, 2010-04-26} Assets:US:Vanguard:Cash -239.94 USD 2010-04-26 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.323 RGAGX {272.20 USD, 2010-04-26} Assets:US:Vanguard:Cash -360.12 USD 2010-05-07 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2010-05-10 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.161 VBMPX {151.82 USD, 2010-05-10} Assets:US:Vanguard:Cash -479.90 USD 2010-05-10 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.634 RGAGX {273.32 USD, 2010-05-10} Assets:US:Vanguard:Cash -719.92 USD 2010-05-10 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.581 VBMPX {151.82 USD, 2010-05-10} Assets:US:Vanguard:Cash -240.03 USD 2010-05-10 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.317 RGAGX {273.32 USD, 2010-05-10} Assets:US:Vanguard:Cash -359.96 USD 2010-05-21 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2010-05-24 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.171 VBMPX {151.37 USD, 2010-05-24} Assets:US:Vanguard:Cash -479.99 USD 2010-05-24 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.598 RGAGX {277.19 USD, 2010-05-24} Assets:US:Vanguard:Cash -720.14 USD 2010-05-24 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.585 VBMPX {151.37 USD, 2010-05-24} Assets:US:Vanguard:Cash -239.92 USD 2010-05-24 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.299 RGAGX {277.19 USD, 2010-05-24} Assets:US:Vanguard:Cash -360.07 USD 2010-06-04 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2010-06-07 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.191 VBMPX {150.41 USD, 2010-06-07} Assets:US:Vanguard:Cash -479.96 USD 2010-06-07 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.598 RGAGX {277.11 USD, 2010-06-07} Assets:US:Vanguard:Cash -719.93 USD 2010-06-07 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.596 VBMPX {150.41 USD, 2010-06-07} Assets:US:Vanguard:Cash -240.05 USD 2010-06-07 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.299 RGAGX {277.11 USD, 2010-06-07} Assets:US:Vanguard:Cash -359.97 USD 2010-06-18 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2010-06-21 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 3.074 VBMPX {156.18 USD, 2010-06-21} Assets:US:Vanguard:Cash -480.10 USD 2010-06-21 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.569 RGAGX {280.33 USD, 2010-06-21} Assets:US:Vanguard:Cash -720.17 USD 2010-06-21 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.536 VBMPX {156.18 USD, 2010-06-21} Assets:US:Vanguard:Cash -239.89 USD 2010-06-21 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.284 RGAGX {280.33 USD, 2010-06-21} Assets:US:Vanguard:Cash -359.94 USD 2010-07-02 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2010-07-05 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.962 VBMPX {162.05 USD, 2010-07-05} Assets:US:Vanguard:Cash -479.99 USD 2010-07-05 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.502 RGAGX {287.80 USD, 2010-07-05} Assets:US:Vanguard:Cash -720.08 USD 2010-07-05 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.481 VBMPX {162.05 USD, 2010-07-05} Assets:US:Vanguard:Cash -240.00 USD 2010-07-05 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.251 RGAGX {287.80 USD, 2010-07-05} Assets:US:Vanguard:Cash -360.04 USD 2010-07-16 * "Employer match for contribution" Assets:US:Vanguard:Cash 450.00 USD Income:US:Hooli:Match401k -450.00 USD 2010-07-19 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.171 VBMPX {165.81 USD, 2010-07-19} Assets:US:Vanguard:Cash -359.97 USD 2010-07-19 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.897 RGAGX {284.63 USD, 2010-07-19} Assets:US:Vanguard:Cash -539.94 USD 2010-07-19 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.086 VBMPX {165.81 USD, 2010-07-19} Assets:US:Vanguard:Cash -180.07 USD 2010-07-19 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.949 RGAGX {284.63 USD, 2010-07-19} Assets:US:Vanguard:Cash -270.11 USD 2011-01-14 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2011-01-17 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.975 VBMPX {161.30 USD, 2011-01-17} Assets:US:Vanguard:Cash -479.87 USD 2011-01-17 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.440 RGAGX {294.97 USD, 2011-01-17} Assets:US:Vanguard:Cash -719.73 USD 2011-01-17 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.488 VBMPX {161.30 USD, 2011-01-17} Assets:US:Vanguard:Cash -240.01 USD 2011-01-17 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.221 RGAGX {294.97 USD, 2011-01-17} Assets:US:Vanguard:Cash -360.16 USD 2011-01-28 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2011-01-31 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.922 VBMPX {164.27 USD, 2011-01-31} Assets:US:Vanguard:Cash -480.00 USD 2011-01-31 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.452 RGAGX {293.68 USD, 2011-01-31} Assets:US:Vanguard:Cash -720.10 USD 2011-01-31 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.461 VBMPX {164.27 USD, 2011-01-31} Assets:US:Vanguard:Cash -240.00 USD 2011-01-31 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.226 RGAGX {293.68 USD, 2011-01-31} Assets:US:Vanguard:Cash -360.05 USD 2011-02-11 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2011-02-14 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.862 VBMPX {167.70 USD, 2011-02-14} Assets:US:Vanguard:Cash -479.96 USD 2011-02-14 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.450 RGAGX {293.86 USD, 2011-02-14} Assets:US:Vanguard:Cash -719.96 USD 2011-02-14 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.431 VBMPX {167.70 USD, 2011-02-14} Assets:US:Vanguard:Cash -239.98 USD 2011-02-14 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.225 RGAGX {293.86 USD, 2011-02-14} Assets:US:Vanguard:Cash -359.98 USD 2011-02-25 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2011-02-28 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.811 VBMPX {170.74 USD, 2011-02-28} Assets:US:Vanguard:Cash -479.95 USD 2011-02-28 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.468 RGAGX {291.68 USD, 2011-02-28} Assets:US:Vanguard:Cash -719.87 USD 2011-02-28 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.406 VBMPX {170.74 USD, 2011-02-28} Assets:US:Vanguard:Cash -240.06 USD 2011-02-28 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.235 RGAGX {291.68 USD, 2011-02-28} Assets:US:Vanguard:Cash -360.22 USD 2011-03-11 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2011-03-14 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.803 VBMPX {171.25 USD, 2011-03-14} Assets:US:Vanguard:Cash -480.01 USD 2011-03-14 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.457 RGAGX {292.98 USD, 2011-03-14} Assets:US:Vanguard:Cash -719.85 USD 2011-03-14 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.402 VBMPX {171.25 USD, 2011-03-14} Assets:US:Vanguard:Cash -240.09 USD 2011-03-14 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.229 RGAGX {292.98 USD, 2011-03-14} Assets:US:Vanguard:Cash -360.07 USD 2011-03-25 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2011-03-28 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.892 VBMPX {165.97 USD, 2011-03-28} Assets:US:Vanguard:Cash -479.99 USD 2011-03-28 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.444 RGAGX {294.58 USD, 2011-03-28} Assets:US:Vanguard:Cash -719.95 USD 2011-03-28 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.446 VBMPX {165.97 USD, 2011-03-28} Assets:US:Vanguard:Cash -239.99 USD 2011-03-28 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.222 RGAGX {294.58 USD, 2011-03-28} Assets:US:Vanguard:Cash -359.98 USD 2011-04-08 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2011-04-11 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.823 VBMPX {170.00 USD, 2011-04-11} Assets:US:Vanguard:Cash -479.91 USD 2011-04-11 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.445 RGAGX {294.50 USD, 2011-04-11} Assets:US:Vanguard:Cash -720.05 USD 2011-04-11 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.412 VBMPX {170.00 USD, 2011-04-11} Assets:US:Vanguard:Cash -240.04 USD 2011-04-11 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.222 RGAGX {294.50 USD, 2011-04-11} Assets:US:Vanguard:Cash -359.88 USD 2011-04-22 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2011-04-25 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.835 VBMPX {169.32 USD, 2011-04-25} Assets:US:Vanguard:Cash -480.02 USD 2011-04-25 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.361 RGAGX {305.02 USD, 2011-04-25} Assets:US:Vanguard:Cash -720.15 USD 2011-04-25 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.417 VBMPX {169.32 USD, 2011-04-25} Assets:US:Vanguard:Cash -239.93 USD 2011-04-25 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.180 RGAGX {305.02 USD, 2011-04-25} Assets:US:Vanguard:Cash -359.92 USD 2011-05-06 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2011-05-09 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.780 VBMPX {172.70 USD, 2011-05-09} Assets:US:Vanguard:Cash -480.11 USD 2011-05-09 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.342 RGAGX {307.46 USD, 2011-05-09} Assets:US:Vanguard:Cash -720.07 USD 2011-05-09 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.389 VBMPX {172.70 USD, 2011-05-09} Assets:US:Vanguard:Cash -239.88 USD 2011-05-09 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.171 RGAGX {307.46 USD, 2011-05-09} Assets:US:Vanguard:Cash -360.04 USD 2011-05-20 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2011-05-23 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.732 VBMPX {175.69 USD, 2011-05-23} Assets:US:Vanguard:Cash -479.99 USD 2011-05-23 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.278 RGAGX {316.04 USD, 2011-05-23} Assets:US:Vanguard:Cash -719.94 USD 2011-05-23 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.366 VBMPX {175.69 USD, 2011-05-23} Assets:US:Vanguard:Cash -239.99 USD 2011-05-23 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.139 RGAGX {316.04 USD, 2011-05-23} Assets:US:Vanguard:Cash -359.97 USD 2011-06-03 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2011-06-06 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.823 VBMPX {170.02 USD, 2011-06-06} Assets:US:Vanguard:Cash -479.97 USD 2011-06-06 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.305 RGAGX {312.33 USD, 2011-06-06} Assets:US:Vanguard:Cash -719.92 USD 2011-06-06 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.412 VBMPX {170.02 USD, 2011-06-06} Assets:US:Vanguard:Cash -240.07 USD 2011-06-06 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.153 RGAGX {312.33 USD, 2011-06-06} Assets:US:Vanguard:Cash -360.12 USD 2011-06-17 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2011-06-20 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.793 VBMPX {171.85 USD, 2011-06-20} Assets:US:Vanguard:Cash -479.98 USD 2011-06-20 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.209 RGAGX {325.96 USD, 2011-06-20} Assets:US:Vanguard:Cash -720.05 USD 2011-06-20 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.396 VBMPX {171.85 USD, 2011-06-20} Assets:US:Vanguard:Cash -239.90 USD 2011-06-20 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.104 RGAGX {325.96 USD, 2011-06-20} Assets:US:Vanguard:Cash -359.86 USD 2011-07-01 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2011-07-04 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.776 VBMPX {172.97 USD, 2011-07-04} Assets:US:Vanguard:Cash -480.16 USD 2011-07-04 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 2.168 RGAGX {332.10 USD, 2011-07-04} Assets:US:Vanguard:Cash -719.99 USD 2011-07-04 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.388 VBMPX {172.97 USD, 2011-07-04} Assets:US:Vanguard:Cash -240.08 USD 2011-07-04 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.084 RGAGX {332.10 USD, 2011-07-04} Assets:US:Vanguard:Cash -360.00 USD 2011-07-15 * "Employer match for contribution" Assets:US:Vanguard:Cash 450.00 USD Income:US:Hooli:Match401k -450.00 USD 2011-07-18 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.234 VBMPX {161.16 USD, 2011-07-18} Assets:US:Vanguard:Cash -360.03 USD 2011-07-18 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.598 RGAGX {337.85 USD, 2011-07-18} Assets:US:Vanguard:Cash -539.88 USD 2011-07-18 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.117 VBMPX {161.16 USD, 2011-07-18} Assets:US:Vanguard:Cash -180.02 USD 2011-07-18 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.799 RGAGX {337.85 USD, 2011-07-18} Assets:US:Vanguard:Cash -269.94 USD 2012-01-13 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2012-01-16 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.409 VBMPX {199.24 USD, 2012-01-16} Assets:US:Vanguard:Cash -479.97 USD 2012-01-16 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.957 RGAGX {367.90 USD, 2012-01-16} Assets:US:Vanguard:Cash -719.98 USD 2012-01-16 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.205 VBMPX {199.24 USD, 2012-01-16} Assets:US:Vanguard:Cash -240.08 USD 2012-01-16 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.979 RGAGX {367.90 USD, 2012-01-16} Assets:US:Vanguard:Cash -360.17 USD 2012-01-27 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2012-01-30 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.338 VBMPX {205.30 USD, 2012-01-30} Assets:US:Vanguard:Cash -479.99 USD 2012-01-30 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.973 RGAGX {364.94 USD, 2012-01-30} Assets:US:Vanguard:Cash -720.03 USD 2012-01-30 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.169 VBMPX {205.30 USD, 2012-01-30} Assets:US:Vanguard:Cash -240.00 USD 2012-01-30 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.986 RGAGX {364.94 USD, 2012-01-30} Assets:US:Vanguard:Cash -359.83 USD 2012-02-10 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2012-02-13 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.233 VBMPX {214.99 USD, 2012-02-13} Assets:US:Vanguard:Cash -480.07 USD 2012-02-13 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.911 RGAGX {376.79 USD, 2012-02-13} Assets:US:Vanguard:Cash -720.05 USD 2012-02-13 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.116 VBMPX {214.99 USD, 2012-02-13} Assets:US:Vanguard:Cash -239.93 USD 2012-02-13 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.955 RGAGX {376.79 USD, 2012-02-13} Assets:US:Vanguard:Cash -359.83 USD 2012-02-24 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2012-02-27 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.229 VBMPX {215.38 USD, 2012-02-27} Assets:US:Vanguard:Cash -480.08 USD 2012-02-27 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.920 RGAGX {374.98 USD, 2012-02-27} Assets:US:Vanguard:Cash -719.96 USD 2012-02-27 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.115 VBMPX {215.38 USD, 2012-02-27} Assets:US:Vanguard:Cash -240.15 USD 2012-02-27 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.960 RGAGX {374.98 USD, 2012-02-27} Assets:US:Vanguard:Cash -359.98 USD 2012-03-09 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2012-03-12 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.230 VBMPX {215.25 USD, 2012-03-12} Assets:US:Vanguard:Cash -480.01 USD 2012-03-12 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.926 RGAGX {373.79 USD, 2012-03-12} Assets:US:Vanguard:Cash -719.92 USD 2012-03-12 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.115 VBMPX {215.25 USD, 2012-03-12} Assets:US:Vanguard:Cash -240.00 USD 2012-03-12 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.963 RGAGX {373.79 USD, 2012-03-12} Assets:US:Vanguard:Cash -359.96 USD 2012-03-23 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2012-03-26 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.174 VBMPX {220.85 USD, 2012-03-26} Assets:US:Vanguard:Cash -480.13 USD 2012-03-26 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.947 RGAGX {369.90 USD, 2012-03-26} Assets:US:Vanguard:Cash -720.20 USD 2012-03-26 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.086 VBMPX {220.85 USD, 2012-03-26} Assets:US:Vanguard:Cash -239.84 USD 2012-03-26 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.973 RGAGX {369.90 USD, 2012-03-26} Assets:US:Vanguard:Cash -359.91 USD 2012-04-06 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2012-04-09 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.181 VBMPX {220.06 USD, 2012-04-09} Assets:US:Vanguard:Cash -479.95 USD 2012-04-09 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.977 RGAGX {364.23 USD, 2012-04-09} Assets:US:Vanguard:Cash -720.08 USD 2012-04-09 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.091 VBMPX {220.06 USD, 2012-04-09} Assets:US:Vanguard:Cash -240.09 USD 2012-04-09 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.988 RGAGX {364.23 USD, 2012-04-09} Assets:US:Vanguard:Cash -359.86 USD 2012-04-20 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2012-04-23 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.219 VBMPX {216.30 USD, 2012-04-23} Assets:US:Vanguard:Cash -479.97 USD 2012-04-23 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.938 RGAGX {371.45 USD, 2012-04-23} Assets:US:Vanguard:Cash -719.87 USD 2012-04-23 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.110 VBMPX {216.30 USD, 2012-04-23} Assets:US:Vanguard:Cash -240.09 USD 2012-04-23 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.970 RGAGX {371.45 USD, 2012-04-23} Assets:US:Vanguard:Cash -360.31 USD 2012-05-04 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2012-05-07 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.270 VBMPX {211.40 USD, 2012-05-07} Assets:US:Vanguard:Cash -479.88 USD 2012-05-07 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.946 RGAGX {369.99 USD, 2012-05-07} Assets:US:Vanguard:Cash -720.00 USD 2012-05-07 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.135 VBMPX {211.40 USD, 2012-05-07} Assets:US:Vanguard:Cash -239.94 USD 2012-05-07 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.973 RGAGX {369.99 USD, 2012-05-07} Assets:US:Vanguard:Cash -360.00 USD 2012-05-18 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2012-05-21 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.280 VBMPX {210.50 USD, 2012-05-21} Assets:US:Vanguard:Cash -479.94 USD 2012-05-21 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.944 RGAGX {370.37 USD, 2012-05-21} Assets:US:Vanguard:Cash -720.00 USD 2012-05-21 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.140 VBMPX {210.50 USD, 2012-05-21} Assets:US:Vanguard:Cash -239.97 USD 2012-05-21 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.972 RGAGX {370.37 USD, 2012-05-21} Assets:US:Vanguard:Cash -360.00 USD 2012-06-01 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2012-06-04 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.263 VBMPX {212.15 USD, 2012-06-04} Assets:US:Vanguard:Cash -480.10 USD 2012-06-04 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.907 RGAGX {377.54 USD, 2012-06-04} Assets:US:Vanguard:Cash -719.97 USD 2012-06-04 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.131 VBMPX {212.15 USD, 2012-06-04} Assets:US:Vanguard:Cash -239.94 USD 2012-06-04 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.954 RGAGX {377.54 USD, 2012-06-04} Assets:US:Vanguard:Cash -360.17 USD 2012-06-15 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2012-06-18 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.307 VBMPX {208.09 USD, 2012-06-18} Assets:US:Vanguard:Cash -480.06 USD 2012-06-18 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.901 RGAGX {378.62 USD, 2012-06-18} Assets:US:Vanguard:Cash -719.76 USD 2012-06-18 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.154 VBMPX {208.09 USD, 2012-06-18} Assets:US:Vanguard:Cash -240.14 USD 2012-06-18 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.951 RGAGX {378.62 USD, 2012-06-18} Assets:US:Vanguard:Cash -360.07 USD 2012-06-29 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2012-07-02 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.270 VBMPX {211.46 USD, 2012-07-02} Assets:US:Vanguard:Cash -480.01 USD 2012-07-02 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.948 RGAGX {369.57 USD, 2012-07-02} Assets:US:Vanguard:Cash -719.92 USD 2012-07-02 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.135 VBMPX {211.46 USD, 2012-07-02} Assets:US:Vanguard:Cash -240.01 USD 2012-07-02 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.974 RGAGX {369.57 USD, 2012-07-02} Assets:US:Vanguard:Cash -359.96 USD 2012-07-13 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2012-07-16 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.267 VBMPX {211.69 USD, 2012-07-16} Assets:US:Vanguard:Cash -479.90 USD 2012-07-16 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.922 RGAGX {374.63 USD, 2012-07-16} Assets:US:Vanguard:Cash -720.04 USD 2012-07-16 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.134 VBMPX {211.69 USD, 2012-07-16} Assets:US:Vanguard:Cash -240.06 USD 2012-07-16 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.961 RGAGX {374.63 USD, 2012-07-16} Assets:US:Vanguard:Cash -360.02 USD 2012-07-27 * "Employer match for contribution" Assets:US:Vanguard:Cash 100.00 USD Income:US:Hooli:Match401k -100.00 USD 2012-07-30 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.372 VBMPX {215.18 USD, 2012-07-30} Assets:US:Vanguard:Cash -80.05 USD 2012-07-30 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.316 RGAGX {379.86 USD, 2012-07-30} Assets:US:Vanguard:Cash -120.04 USD 2012-07-30 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.186 VBMPX {215.18 USD, 2012-07-30} Assets:US:Vanguard:Cash -40.02 USD 2012-07-30 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.158 RGAGX {379.86 USD, 2012-07-30} Assets:US:Vanguard:Cash -60.02 USD 2013-01-11 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2013-01-14 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.996 VBMPX {240.44 USD, 2013-01-14} Assets:US:Vanguard:Cash -479.92 USD 2013-01-14 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.677 RGAGX {429.27 USD, 2013-01-14} Assets:US:Vanguard:Cash -719.89 USD 2013-01-14 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.998 VBMPX {240.44 USD, 2013-01-14} Assets:US:Vanguard:Cash -239.96 USD 2013-01-14 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.839 RGAGX {429.27 USD, 2013-01-14} Assets:US:Vanguard:Cash -360.16 USD 2013-01-25 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2013-01-28 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.968 VBMPX {243.83 USD, 2013-01-28} Assets:US:Vanguard:Cash -479.86 USD 2013-01-28 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.710 RGAGX {421.05 USD, 2013-01-28} Assets:US:Vanguard:Cash -720.00 USD 2013-01-28 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.984 VBMPX {243.83 USD, 2013-01-28} Assets:US:Vanguard:Cash -239.93 USD 2013-01-28 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.855 RGAGX {421.05 USD, 2013-01-28} Assets:US:Vanguard:Cash -360.00 USD 2013-02-08 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2013-02-11 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.925 VBMPX {249.39 USD, 2013-02-11} Assets:US:Vanguard:Cash -480.08 USD 2013-02-11 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.741 RGAGX {413.48 USD, 2013-02-11} Assets:US:Vanguard:Cash -719.87 USD 2013-02-11 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.963 VBMPX {249.39 USD, 2013-02-11} Assets:US:Vanguard:Cash -240.16 USD 2013-02-11 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.871 RGAGX {413.48 USD, 2013-02-11} Assets:US:Vanguard:Cash -360.14 USD 2013-02-22 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2013-02-25 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.018 VBMPX {237.78 USD, 2013-02-25} Assets:US:Vanguard:Cash -479.84 USD 2013-02-25 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.720 RGAGX {418.62 USD, 2013-02-25} Assets:US:Vanguard:Cash -720.03 USD 2013-02-25 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.009 VBMPX {237.78 USD, 2013-02-25} Assets:US:Vanguard:Cash -239.92 USD 2013-02-25 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.860 RGAGX {418.62 USD, 2013-02-25} Assets:US:Vanguard:Cash -360.01 USD 2013-03-08 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2013-03-11 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.029 VBMPX {236.58 USD, 2013-03-11} Assets:US:Vanguard:Cash -480.02 USD 2013-03-11 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.694 RGAGX {424.97 USD, 2013-03-11} Assets:US:Vanguard:Cash -719.90 USD 2013-03-11 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.015 VBMPX {236.58 USD, 2013-03-11} Assets:US:Vanguard:Cash -240.13 USD 2013-03-11 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.847 RGAGX {424.97 USD, 2013-03-11} Assets:US:Vanguard:Cash -359.95 USD 2013-03-22 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2013-03-25 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.114 VBMPX {227.04 USD, 2013-03-25} Assets:US:Vanguard:Cash -479.96 USD 2013-03-25 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.666 RGAGX {432.32 USD, 2013-03-25} Assets:US:Vanguard:Cash -720.25 USD 2013-03-25 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.057 VBMPX {227.04 USD, 2013-03-25} Assets:US:Vanguard:Cash -239.98 USD 2013-03-25 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.832 RGAGX {432.32 USD, 2013-03-25} Assets:US:Vanguard:Cash -359.69 USD 2013-04-05 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2013-04-08 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.169 VBMPX {221.29 USD, 2013-04-08} Assets:US:Vanguard:Cash -479.98 USD 2013-04-08 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.640 RGAGX {439.16 USD, 2013-04-08} Assets:US:Vanguard:Cash -720.22 USD 2013-04-08 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.084 VBMPX {221.29 USD, 2013-04-08} Assets:US:Vanguard:Cash -239.88 USD 2013-04-08 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.820 RGAGX {439.16 USD, 2013-04-08} Assets:US:Vanguard:Cash -360.11 USD 2013-04-19 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2013-04-22 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.261 VBMPX {212.29 USD, 2013-04-22} Assets:US:Vanguard:Cash -479.99 USD 2013-04-22 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.633 RGAGX {440.93 USD, 2013-04-22} Assets:US:Vanguard:Cash -720.04 USD 2013-04-22 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.130 VBMPX {212.29 USD, 2013-04-22} Assets:US:Vanguard:Cash -239.89 USD 2013-04-22 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.816 RGAGX {440.93 USD, 2013-04-22} Assets:US:Vanguard:Cash -359.80 USD 2013-05-03 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2013-05-06 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.183 VBMPX {219.92 USD, 2013-05-06} Assets:US:Vanguard:Cash -480.09 USD 2013-05-06 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.605 RGAGX {448.67 USD, 2013-05-06} Assets:US:Vanguard:Cash -720.12 USD 2013-05-06 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.091 VBMPX {219.92 USD, 2013-05-06} Assets:US:Vanguard:Cash -239.93 USD 2013-05-06 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.802 RGAGX {448.67 USD, 2013-05-06} Assets:US:Vanguard:Cash -359.83 USD 2013-05-17 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2013-05-20 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.213 VBMPX {216.92 USD, 2013-05-20} Assets:US:Vanguard:Cash -480.04 USD 2013-05-20 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.578 RGAGX {456.42 USD, 2013-05-20} Assets:US:Vanguard:Cash -720.23 USD 2013-05-20 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.106 VBMPX {216.92 USD, 2013-05-20} Assets:US:Vanguard:Cash -239.91 USD 2013-05-20 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.789 RGAGX {456.42 USD, 2013-05-20} Assets:US:Vanguard:Cash -360.12 USD 2013-05-31 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2013-06-03 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.122 VBMPX {226.25 USD, 2013-06-03} Assets:US:Vanguard:Cash -480.10 USD 2013-06-03 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.593 RGAGX {452.08 USD, 2013-06-03} Assets:US:Vanguard:Cash -720.16 USD 2013-06-03 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.060 VBMPX {226.25 USD, 2013-06-03} Assets:US:Vanguard:Cash -239.82 USD 2013-06-03 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.796 RGAGX {452.08 USD, 2013-06-03} Assets:US:Vanguard:Cash -359.86 USD 2013-06-14 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2013-06-17 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.088 VBMPX {229.89 USD, 2013-06-17} Assets:US:Vanguard:Cash -480.01 USD 2013-06-17 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.612 RGAGX {446.68 USD, 2013-06-17} Assets:US:Vanguard:Cash -720.05 USD 2013-06-17 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.044 VBMPX {229.89 USD, 2013-06-17} Assets:US:Vanguard:Cash -240.01 USD 2013-06-17 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.806 RGAGX {446.68 USD, 2013-06-17} Assets:US:Vanguard:Cash -360.02 USD 2013-06-28 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2013-07-01 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.084 VBMPX {230.37 USD, 2013-07-01} Assets:US:Vanguard:Cash -480.09 USD 2013-07-01 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.606 RGAGX {448.37 USD, 2013-07-01} Assets:US:Vanguard:Cash -720.08 USD 2013-07-01 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.041 VBMPX {230.37 USD, 2013-07-01} Assets:US:Vanguard:Cash -239.82 USD 2013-07-01 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.803 RGAGX {448.37 USD, 2013-07-01} Assets:US:Vanguard:Cash -360.04 USD 2013-07-12 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2013-07-15 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 2.066 VBMPX {232.33 USD, 2013-07-15} Assets:US:Vanguard:Cash -479.99 USD 2013-07-15 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.578 RGAGX {456.29 USD, 2013-07-15} Assets:US:Vanguard:Cash -720.03 USD 2013-07-15 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.033 VBMPX {232.33 USD, 2013-07-15} Assets:US:Vanguard:Cash -240.00 USD 2013-07-15 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.789 RGAGX {456.29 USD, 2013-07-15} Assets:US:Vanguard:Cash -360.01 USD 2013-07-26 * "Employer match for contribution" Assets:US:Vanguard:Cash 350.00 USD Income:US:Hooli:Match401k -350.00 USD 2013-07-29 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.178 VBMPX {237.59 USD, 2013-07-29} Assets:US:Vanguard:Cash -279.88 USD 2013-07-29 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.901 RGAGX {465.94 USD, 2013-07-29} Assets:US:Vanguard:Cash -419.81 USD 2013-07-29 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.590 VBMPX {237.59 USD, 2013-07-29} Assets:US:Vanguard:Cash -140.18 USD 2013-07-29 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.451 RGAGX {465.94 USD, 2013-07-29} Assets:US:Vanguard:Cash -210.14 USD 2014-01-10 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2014-01-13 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.958 VBMPX {245.18 USD, 2014-01-13} Assets:US:Vanguard:Cash -480.06 USD 2014-01-13 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.459 RGAGX {493.41 USD, 2014-01-13} Assets:US:Vanguard:Cash -719.89 USD 2014-01-13 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.979 VBMPX {245.18 USD, 2014-01-13} Assets:US:Vanguard:Cash -240.03 USD 2014-01-13 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.730 RGAGX {493.41 USD, 2014-01-13} Assets:US:Vanguard:Cash -360.19 USD 2014-01-24 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2014-01-27 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.930 VBMPX {248.68 USD, 2014-01-27} Assets:US:Vanguard:Cash -479.95 USD 2014-01-27 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.468 RGAGX {490.32 USD, 2014-01-27} Assets:US:Vanguard:Cash -719.79 USD 2014-01-27 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.965 VBMPX {248.68 USD, 2014-01-27} Assets:US:Vanguard:Cash -239.98 USD 2014-01-27 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.734 RGAGX {490.32 USD, 2014-01-27} Assets:US:Vanguard:Cash -359.89 USD 2014-02-07 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2014-02-10 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.954 VBMPX {245.72 USD, 2014-02-10} Assets:US:Vanguard:Cash -480.14 USD 2014-02-10 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.458 RGAGX {493.71 USD, 2014-02-10} Assets:US:Vanguard:Cash -719.83 USD 2014-02-10 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.977 VBMPX {245.72 USD, 2014-02-10} Assets:US:Vanguard:Cash -240.07 USD 2014-02-10 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.729 RGAGX {493.71 USD, 2014-02-10} Assets:US:Vanguard:Cash -359.91 USD 2014-02-21 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2014-02-24 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.959 VBMPX {245.00 USD, 2014-02-24} Assets:US:Vanguard:Cash -479.96 USD 2014-02-24 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.492 RGAGX {482.51 USD, 2014-02-24} Assets:US:Vanguard:Cash -719.90 USD 2014-02-24 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.980 VBMPX {245.00 USD, 2014-02-24} Assets:US:Vanguard:Cash -240.10 USD 2014-02-24 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.746 RGAGX {482.51 USD, 2014-02-24} Assets:US:Vanguard:Cash -359.95 USD 2014-03-07 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2014-03-10 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.930 VBMPX {248.79 USD, 2014-03-10} Assets:US:Vanguard:Cash -480.16 USD 2014-03-10 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.471 RGAGX {489.62 USD, 2014-03-10} Assets:US:Vanguard:Cash -720.23 USD 2014-03-10 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.964 VBMPX {248.79 USD, 2014-03-10} Assets:US:Vanguard:Cash -239.83 USD 2014-03-10 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.735 RGAGX {489.62 USD, 2014-03-10} Assets:US:Vanguard:Cash -359.87 USD 2014-03-21 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2014-03-24 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.914 VBMPX {250.87 USD, 2014-03-24} Assets:US:Vanguard:Cash -480.17 USD 2014-03-24 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.466 RGAGX {491.19 USD, 2014-03-24} Assets:US:Vanguard:Cash -720.08 USD 2014-03-24 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.957 VBMPX {250.87 USD, 2014-03-24} Assets:US:Vanguard:Cash -240.08 USD 2014-03-24 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.733 RGAGX {491.19 USD, 2014-03-24} Assets:US:Vanguard:Cash -360.04 USD 2014-04-04 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2014-04-07 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.847 VBMPX {259.78 USD, 2014-04-07} Assets:US:Vanguard:Cash -479.81 USD 2014-04-07 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.463 RGAGX {492.19 USD, 2014-04-07} Assets:US:Vanguard:Cash -720.07 USD 2014-04-07 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.924 VBMPX {259.78 USD, 2014-04-07} Assets:US:Vanguard:Cash -240.04 USD 2014-04-07 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.731 RGAGX {492.19 USD, 2014-04-07} Assets:US:Vanguard:Cash -359.79 USD 2014-04-18 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2014-04-21 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.835 VBMPX {261.60 USD, 2014-04-21} Assets:US:Vanguard:Cash -480.04 USD 2014-04-21 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.499 RGAGX {480.27 USD, 2014-04-21} Assets:US:Vanguard:Cash -719.92 USD 2014-04-21 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.918 VBMPX {261.60 USD, 2014-04-21} Assets:US:Vanguard:Cash -240.15 USD 2014-04-21 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.750 RGAGX {480.27 USD, 2014-04-21} Assets:US:Vanguard:Cash -360.20 USD 2014-05-02 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2014-05-05 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.849 VBMPX {259.48 USD, 2014-05-05} Assets:US:Vanguard:Cash -479.78 USD 2014-05-05 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.468 RGAGX {490.27 USD, 2014-05-05} Assets:US:Vanguard:Cash -719.72 USD 2014-05-05 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.925 VBMPX {259.48 USD, 2014-05-05} Assets:US:Vanguard:Cash -240.02 USD 2014-05-05 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.735 RGAGX {490.27 USD, 2014-05-05} Assets:US:Vanguard:Cash -360.35 USD 2014-05-16 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2014-05-19 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.852 VBMPX {259.09 USD, 2014-05-19} Assets:US:Vanguard:Cash -479.83 USD 2014-05-19 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.502 RGAGX {479.24 USD, 2014-05-19} Assets:US:Vanguard:Cash -719.82 USD 2014-05-19 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.927 VBMPX {259.09 USD, 2014-05-19} Assets:US:Vanguard:Cash -240.18 USD 2014-05-19 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.752 RGAGX {479.24 USD, 2014-05-19} Assets:US:Vanguard:Cash -360.39 USD 2014-05-30 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2014-06-02 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.832 VBMPX {261.93 USD, 2014-06-02} Assets:US:Vanguard:Cash -479.86 USD 2014-06-02 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.540 RGAGX {467.27 USD, 2014-06-02} Assets:US:Vanguard:Cash -719.60 USD 2014-06-02 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.917 VBMPX {261.93 USD, 2014-06-02} Assets:US:Vanguard:Cash -240.19 USD 2014-06-02 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.771 RGAGX {467.27 USD, 2014-06-02} Assets:US:Vanguard:Cash -360.27 USD 2014-06-13 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2014-06-16 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.863 VBMPX {257.59 USD, 2014-06-16} Assets:US:Vanguard:Cash -479.89 USD 2014-06-16 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.553 RGAGX {463.42 USD, 2014-06-16} Assets:US:Vanguard:Cash -719.69 USD 2014-06-16 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.932 VBMPX {257.59 USD, 2014-06-16} Assets:US:Vanguard:Cash -240.07 USD 2014-06-16 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.777 RGAGX {463.42 USD, 2014-06-16} Assets:US:Vanguard:Cash -360.08 USD 2014-06-27 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2014-06-30 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.896 VBMPX {253.20 USD, 2014-06-30} Assets:US:Vanguard:Cash -480.07 USD 2014-06-30 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.569 RGAGX {458.92 USD, 2014-06-30} Assets:US:Vanguard:Cash -720.05 USD 2014-06-30 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.948 VBMPX {253.20 USD, 2014-06-30} Assets:US:Vanguard:Cash -240.03 USD 2014-06-30 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.784 RGAGX {458.92 USD, 2014-06-30} Assets:US:Vanguard:Cash -359.79 USD 2014-07-11 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2014-07-14 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.924 VBMPX {249.53 USD, 2014-07-14} Assets:US:Vanguard:Cash -480.10 USD 2014-07-14 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.578 RGAGX {456.35 USD, 2014-07-14} Assets:US:Vanguard:Cash -720.12 USD 2014-07-14 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.962 VBMPX {249.53 USD, 2014-07-14} Assets:US:Vanguard:Cash -240.05 USD 2014-07-14 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.789 RGAGX {456.35 USD, 2014-07-14} Assets:US:Vanguard:Cash -360.06 USD 2014-07-25 * "Employer match for contribution" Assets:US:Vanguard:Cash 350.00 USD Income:US:Hooli:Match401k -350.00 USD 2014-07-28 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.164 VBMPX {240.39 USD, 2014-07-28} Assets:US:Vanguard:Cash -279.81 USD 2014-07-28 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.933 RGAGX {450.07 USD, 2014-07-28} Assets:US:Vanguard:Cash -419.92 USD 2014-07-28 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.582 VBMPX {240.39 USD, 2014-07-28} Assets:US:Vanguard:Cash -139.91 USD 2014-07-28 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.467 RGAGX {450.07 USD, 2014-07-28} Assets:US:Vanguard:Cash -210.18 USD 2015-01-09 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2015-01-12 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.962 VBMPX {244.58 USD, 2015-01-12} Assets:US:Vanguard:Cash -479.87 USD 2015-01-12 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.423 RGAGX {506.06 USD, 2015-01-12} Assets:US:Vanguard:Cash -720.12 USD 2015-01-12 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.981 VBMPX {244.58 USD, 2015-01-12} Assets:US:Vanguard:Cash -239.93 USD 2015-01-12 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.711 RGAGX {506.06 USD, 2015-01-12} Assets:US:Vanguard:Cash -359.81 USD 2015-01-23 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2015-01-26 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.892 VBMPX {253.80 USD, 2015-01-26} Assets:US:Vanguard:Cash -480.19 USD 2015-01-26 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.399 RGAGX {514.64 USD, 2015-01-26} Assets:US:Vanguard:Cash -719.98 USD 2015-01-26 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.946 VBMPX {253.80 USD, 2015-01-26} Assets:US:Vanguard:Cash -240.09 USD 2015-01-26 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.700 RGAGX {514.64 USD, 2015-01-26} Assets:US:Vanguard:Cash -360.25 USD 2015-02-06 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2015-02-09 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.853 VBMPX {259.03 USD, 2015-02-09} Assets:US:Vanguard:Cash -479.98 USD 2015-02-09 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.376 RGAGX {523.10 USD, 2015-02-09} Assets:US:Vanguard:Cash -719.79 USD 2015-02-09 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.926 VBMPX {259.03 USD, 2015-02-09} Assets:US:Vanguard:Cash -239.86 USD 2015-02-09 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.688 RGAGX {523.10 USD, 2015-02-09} Assets:US:Vanguard:Cash -359.89 USD 2015-02-20 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2015-02-23 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.882 VBMPX {255.14 USD, 2015-02-23} Assets:US:Vanguard:Cash -480.17 USD 2015-02-23 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.343 RGAGX {536.30 USD, 2015-02-23} Assets:US:Vanguard:Cash -720.25 USD 2015-02-23 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.940 VBMPX {255.14 USD, 2015-02-23} Assets:US:Vanguard:Cash -239.83 USD 2015-02-23 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.671 RGAGX {536.30 USD, 2015-02-23} Assets:US:Vanguard:Cash -359.86 USD 2015-03-06 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2015-03-09 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.817 VBMPX {264.20 USD, 2015-03-09} Assets:US:Vanguard:Cash -480.05 USD 2015-03-09 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.341 RGAGX {537.09 USD, 2015-03-09} Assets:US:Vanguard:Cash -720.24 USD 2015-03-09 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.908 VBMPX {264.20 USD, 2015-03-09} Assets:US:Vanguard:Cash -239.89 USD 2015-03-09 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.670 RGAGX {537.09 USD, 2015-03-09} Assets:US:Vanguard:Cash -359.85 USD 2015-03-20 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2015-03-23 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.764 VBMPX {272.05 USD, 2015-03-23} Assets:US:Vanguard:Cash -479.90 USD 2015-03-23 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.349 RGAGX {533.82 USD, 2015-03-23} Assets:US:Vanguard:Cash -720.12 USD 2015-03-23 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.882 VBMPX {272.05 USD, 2015-03-23} Assets:US:Vanguard:Cash -239.95 USD 2015-03-23 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.674 RGAGX {533.82 USD, 2015-03-23} Assets:US:Vanguard:Cash -359.79 USD 2015-04-03 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2015-04-06 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.744 VBMPX {275.32 USD, 2015-04-06} Assets:US:Vanguard:Cash -480.16 USD 2015-04-06 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.339 RGAGX {537.74 USD, 2015-04-06} Assets:US:Vanguard:Cash -720.03 USD 2015-04-06 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.872 VBMPX {275.32 USD, 2015-04-06} Assets:US:Vanguard:Cash -240.08 USD 2015-04-06 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.670 RGAGX {537.74 USD, 2015-04-06} Assets:US:Vanguard:Cash -360.29 USD 2015-04-17 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2015-04-20 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.728 VBMPX {277.75 USD, 2015-04-20} Assets:US:Vanguard:Cash -479.95 USD 2015-04-20 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.296 RGAGX {555.36 USD, 2015-04-20} Assets:US:Vanguard:Cash -719.75 USD 2015-04-20 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.864 VBMPX {277.75 USD, 2015-04-20} Assets:US:Vanguard:Cash -239.98 USD 2015-04-20 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.648 RGAGX {555.36 USD, 2015-04-20} Assets:US:Vanguard:Cash -359.87 USD 2015-05-01 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2015-05-04 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.770 VBMPX {271.19 USD, 2015-05-04} Assets:US:Vanguard:Cash -480.01 USD 2015-05-04 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.252 RGAGX {575.09 USD, 2015-05-04} Assets:US:Vanguard:Cash -720.01 USD 2015-05-04 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.885 VBMPX {271.19 USD, 2015-05-04} Assets:US:Vanguard:Cash -240.00 USD 2015-05-04 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.626 RGAGX {575.09 USD, 2015-05-04} Assets:US:Vanguard:Cash -360.01 USD 2015-05-15 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2015-05-18 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.753 VBMPX {273.84 USD, 2015-05-18} Assets:US:Vanguard:Cash -480.04 USD 2015-05-18 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.266 RGAGX {568.97 USD, 2015-05-18} Assets:US:Vanguard:Cash -720.32 USD 2015-05-18 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.876 VBMPX {273.84 USD, 2015-05-18} Assets:US:Vanguard:Cash -239.88 USD 2015-05-18 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.632 RGAGX {568.97 USD, 2015-05-18} Assets:US:Vanguard:Cash -359.59 USD 2015-05-29 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2015-06-01 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.815 VBMPX {264.48 USD, 2015-06-01} Assets:US:Vanguard:Cash -480.03 USD 2015-06-01 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.269 RGAGX {567.45 USD, 2015-06-01} Assets:US:Vanguard:Cash -720.09 USD 2015-06-01 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.908 VBMPX {264.48 USD, 2015-06-01} Assets:US:Vanguard:Cash -240.15 USD 2015-06-01 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.635 RGAGX {567.45 USD, 2015-06-01} Assets:US:Vanguard:Cash -360.33 USD 2015-06-12 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2015-06-15 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.814 VBMPX {264.55 USD, 2015-06-15} Assets:US:Vanguard:Cash -479.89 USD 2015-06-15 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.229 RGAGX {585.83 USD, 2015-06-15} Assets:US:Vanguard:Cash -719.99 USD 2015-06-15 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.907 VBMPX {264.55 USD, 2015-06-15} Assets:US:Vanguard:Cash -239.95 USD 2015-06-15 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.614 RGAGX {585.83 USD, 2015-06-15} Assets:US:Vanguard:Cash -359.70 USD 2015-06-26 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2015-06-29 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.740 VBMPX {275.91 USD, 2015-06-29} Assets:US:Vanguard:Cash -480.08 USD 2015-06-29 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.247 RGAGX {577.69 USD, 2015-06-29} Assets:US:Vanguard:Cash -720.38 USD 2015-06-29 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.869 VBMPX {275.91 USD, 2015-06-29} Assets:US:Vanguard:Cash -239.77 USD 2015-06-29 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.623 RGAGX {577.69 USD, 2015-06-29} Assets:US:Vanguard:Cash -359.90 USD 2015-07-10 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2015-07-13 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.720 VBMPX {279.07 USD, 2015-07-13} Assets:US:Vanguard:Cash -480.00 USD 2015-07-13 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.238 RGAGX {581.63 USD, 2015-07-13} Assets:US:Vanguard:Cash -720.06 USD 2015-07-13 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.860 VBMPX {279.07 USD, 2015-07-13} Assets:US:Vanguard:Cash -240.00 USD 2015-07-13 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.619 RGAGX {581.63 USD, 2015-07-13} Assets:US:Vanguard:Cash -360.03 USD 2015-07-24 * "Employer match for contribution" Assets:US:Vanguard:Cash 600.00 USD Income:US:Hooli:Match401k -600.00 USD 2015-07-27 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 1.712 VBMPX {280.35 USD, 2015-07-27} Assets:US:Vanguard:Cash -479.96 USD 2015-07-27 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 1.232 RGAGX {584.42 USD, 2015-07-27} Assets:US:Vanguard:Cash -720.01 USD 2015-07-27 * "Investing 40% of cash in VBMPX" Assets:US:Vanguard:VBMPX 0.856 VBMPX {280.35 USD, 2015-07-27} Assets:US:Vanguard:Cash -239.98 USD 2015-07-27 * "Investing 60% of cash in RGAGX" Assets:US:Vanguard:RGAGX 0.616 RGAGX {584.42 USD, 2015-07-27} Assets:US:Vanguard:Cash -360.00 USD * Sources of Income 2008-01-01 open Income:US:Hooli:Salary USD 2008-01-01 open Income:US:Hooli:GroupTermLife USD 2008-01-01 open Income:US:Hooli:Vacation VACHR 2008-01-01 open Assets:US:Hooli:Vacation VACHR 2008-01-01 open Expenses:Vacation VACHR 2008-01-01 open Expenses:Health:Life:GroupTermLife 2008-01-01 open Expenses:Health:Medical:Insurance 2008-01-01 open Expenses:Health:Dental:Insurance 2008-01-01 open Expenses:Health:Vision:Insurance 2008-01-01 event "employer" "Hooli, 1 Carloston Rd, Mountain Beer, CA" 2008-01-03 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2008:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-01-17 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2008:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-01-31 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2008:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-02-14 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2008:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-02-28 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2008:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-03-13 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2008:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-03-27 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2008:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-04-10 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2008:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-04-24 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2008:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-05-08 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2008:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-05-22 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2008:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-06-05 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2008:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-06-19 * "Hooli" "Payroll" Assets:US:BofA:Checking 1450.60 USD Assets:US:Vanguard:Cash 1100.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1100.00 IRAUSD Expenses:Taxes:Y2008:US:Federal:PreTax401k 1100.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-07-03 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-07-17 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-07-31 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-08-14 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-08-28 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-09-11 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-09-25 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-10-09 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-10-23 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-11-06 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-11-20 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-12-04 * "Hooli" "Payroll" Assets:US:BofA:Checking 2589.06 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 243.08 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2008-12-18 * "Hooli" "Payroll" Assets:US:BofA:Checking 2832.14 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2008:US:Medicare 106.62 USD Expenses:Taxes:Y2008:US:Federal 1062.92 USD Expenses:Taxes:Y2008:US:State 365.08 USD Expenses:Taxes:Y2008:US:CityNYC 174.92 USD Expenses:Taxes:Y2008:US:SDI 1.12 USD Expenses:Taxes:Y2008:US:SocSec 0.00 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-01-01 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2009:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-01-15 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2009:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-01-29 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2009:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-02-12 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2009:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-02-26 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2009:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-03-12 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2009:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-03-26 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2009:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-04-09 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2009:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-04-23 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2009:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-05-07 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2009:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-05-21 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2009:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-06-04 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2009:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-06-18 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2009:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-07-02 * "Hooli" "Payroll" Assets:US:BofA:Checking 1650.60 USD Assets:US:Vanguard:Cash 900.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -900.00 IRAUSD Expenses:Taxes:Y2009:US:Federal:PreTax401k 900.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-07-16 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-07-30 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-08-13 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-08-27 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-09-10 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-09-24 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-10-08 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-10-22 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-11-05 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-11-19 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-12-03 * "Hooli" "Payroll" Assets:US:BofA:Checking 2589.06 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 243.08 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-12-17 * "Hooli" "Payroll" Assets:US:BofA:Checking 2832.14 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 0.00 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2009-12-31 * "Hooli" "Payroll" Assets:US:BofA:Checking 2832.14 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2009:US:Medicare 106.62 USD Expenses:Taxes:Y2009:US:Federal 1062.92 USD Expenses:Taxes:Y2009:US:State 365.08 USD Expenses:Taxes:Y2009:US:CityNYC 174.92 USD Expenses:Taxes:Y2009:US:SDI 1.12 USD Expenses:Taxes:Y2009:US:SocSec 0.00 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-01-14 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2010:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-01-28 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2010:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-02-11 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2010:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-02-25 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2010:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-03-11 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2010:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-03-25 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2010:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-04-08 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2010:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-04-22 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2010:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-05-06 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2010:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-05-20 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2010:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-06-03 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2010:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-06-17 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2010:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-07-01 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2010:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-07-15 * "Hooli" "Payroll" Assets:US:BofA:Checking 1650.60 USD Assets:US:Vanguard:Cash 900.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -900.00 IRAUSD Expenses:Taxes:Y2010:US:Federal:PreTax401k 900.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-07-29 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-08-12 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-08-26 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-09-09 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-09-23 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-10-07 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-10-21 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-11-04 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-11-18 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-12-02 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-12-16 * "Hooli" "Payroll" Assets:US:BofA:Checking 2589.06 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 243.08 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2010-12-30 * "Hooli" "Payroll" Assets:US:BofA:Checking 2832.14 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2010:US:Medicare 106.62 USD Expenses:Taxes:Y2010:US:Federal 1062.92 USD Expenses:Taxes:Y2010:US:State 365.08 USD Expenses:Taxes:Y2010:US:CityNYC 174.92 USD Expenses:Taxes:Y2010:US:SDI 1.12 USD Expenses:Taxes:Y2010:US:SocSec 0.00 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-01-13 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2011:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-01-27 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2011:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-02-10 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2011:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-02-24 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2011:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-03-10 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2011:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-03-24 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2011:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-04-07 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2011:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-04-21 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2011:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-05-05 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2011:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-05-19 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2011:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-06-02 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2011:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-06-16 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2011:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-06-30 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2011:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-07-14 * "Hooli" "Payroll" Assets:US:BofA:Checking 1650.60 USD Assets:US:Vanguard:Cash 900.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -900.00 IRAUSD Expenses:Taxes:Y2011:US:Federal:PreTax401k 900.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-07-28 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-08-11 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-08-25 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-09-08 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-09-22 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-10-06 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-10-20 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-11-03 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-11-17 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-12-01 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-12-15 * "Hooli" "Payroll" Assets:US:BofA:Checking 2589.06 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 243.08 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2011-12-29 * "Hooli" "Payroll" Assets:US:BofA:Checking 2832.14 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2011:US:Medicare 106.62 USD Expenses:Taxes:Y2011:US:Federal 1062.92 USD Expenses:Taxes:Y2011:US:State 365.08 USD Expenses:Taxes:Y2011:US:CityNYC 174.92 USD Expenses:Taxes:Y2011:US:SDI 1.12 USD Expenses:Taxes:Y2011:US:SocSec 0.00 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-01-12 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2012:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-01-26 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2012:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-02-09 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2012:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-02-23 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2012:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-03-08 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2012:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-03-22 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2012:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-04-05 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2012:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-04-19 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2012:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-05-03 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2012:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-05-17 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2012:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-05-31 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2012:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-06-14 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2012:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-06-28 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2012:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-07-12 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2012:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-07-26 * "Hooli" "Payroll" Assets:US:BofA:Checking 2350.60 USD Assets:US:Vanguard:Cash 200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -200.00 IRAUSD Expenses:Taxes:Y2012:US:Federal:PreTax401k 200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-08-09 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-08-23 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-09-06 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-09-20 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-10-04 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-10-18 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-11-01 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-11-15 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-11-29 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-12-13 * "Hooli" "Payroll" Assets:US:BofA:Checking 2589.06 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 243.08 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2012-12-27 * "Hooli" "Payroll" Assets:US:BofA:Checking 2832.14 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2012:US:Medicare 106.62 USD Expenses:Taxes:Y2012:US:Federal 1062.92 USD Expenses:Taxes:Y2012:US:State 365.08 USD Expenses:Taxes:Y2012:US:CityNYC 174.92 USD Expenses:Taxes:Y2012:US:SDI 1.12 USD Expenses:Taxes:Y2012:US:SocSec 0.00 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-01-10 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2013:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-01-24 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2013:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-02-07 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2013:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-02-21 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2013:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-03-07 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2013:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-03-21 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2013:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-04-04 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2013:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-04-18 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2013:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-05-02 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2013:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-05-16 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2013:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-05-30 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2013:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-06-13 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2013:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-06-27 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2013:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-07-11 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2013:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-07-25 * "Hooli" "Payroll" Assets:US:BofA:Checking 1850.60 USD Assets:US:Vanguard:Cash 700.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -700.00 IRAUSD Expenses:Taxes:Y2013:US:Federal:PreTax401k 700.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-08-08 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-08-22 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-09-05 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-09-19 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-10-03 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-10-17 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-10-31 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-11-14 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-11-28 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-12-12 * "Hooli" "Payroll" Assets:US:BofA:Checking 2589.06 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 243.08 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2013-12-26 * "Hooli" "Payroll" Assets:US:BofA:Checking 2832.14 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2013:US:Medicare 106.62 USD Expenses:Taxes:Y2013:US:Federal 1062.92 USD Expenses:Taxes:Y2013:US:State 365.08 USD Expenses:Taxes:Y2013:US:CityNYC 174.92 USD Expenses:Taxes:Y2013:US:SDI 1.12 USD Expenses:Taxes:Y2013:US:SocSec 0.00 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-01-09 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2014:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-01-23 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2014:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-02-06 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2014:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-02-20 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2014:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-03-06 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2014:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-03-20 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2014:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-04-03 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2014:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-04-17 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2014:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-05-01 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2014:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-05-15 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2014:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-05-29 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2014:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-06-12 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2014:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-06-26 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2014:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-07-10 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2014:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-07-24 * "Hooli" "Payroll" Assets:US:BofA:Checking 1850.60 USD Assets:US:Vanguard:Cash 700.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -700.00 IRAUSD Expenses:Taxes:Y2014:US:Federal:PreTax401k 700.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-08-07 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-08-21 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-09-04 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-09-18 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-10-02 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-10-16 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-10-30 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-11-13 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-11-27 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-12-11 * "Hooli" "Payroll" Assets:US:BofA:Checking 2589.06 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 243.08 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2014-12-25 * "Hooli" "Payroll" Assets:US:BofA:Checking 2832.14 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2014:US:Medicare 106.62 USD Expenses:Taxes:Y2014:US:Federal 1062.92 USD Expenses:Taxes:Y2014:US:State 365.08 USD Expenses:Taxes:Y2014:US:CityNYC 174.92 USD Expenses:Taxes:Y2014:US:SDI 1.12 USD Expenses:Taxes:Y2014:US:SocSec 0.00 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2015-01-08 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2015-01-22 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2015-02-05 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2015-02-19 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2015-03-05 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2015-03-19 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2015-04-02 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2015-04-16 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2015-04-30 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2015-05-14 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2015-05-28 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2015-06-11 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2015-06-25 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2015-07-09 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2015-07-23 * "Hooli" "Payroll" Assets:US:BofA:Checking 1350.60 USD Assets:US:Vanguard:Cash 1200.00 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k -1200.00 IRAUSD Expenses:Taxes:Y2015:US:Federal:PreTax401k 1200.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2015-08-06 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2015-08-20 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2015-09-03 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2015-09-17 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR 2015-10-01 * "Hooli" "Payroll" Assets:US:BofA:Checking 2550.60 USD Income:US:Hooli:Salary -4615.38 USD Income:US:Hooli:GroupTermLife -24.32 USD Expenses:Health:Life:GroupTermLife 24.32 USD Expenses:Health:Dental:Insurance 2.90 USD Expenses:Health:Medical:Insurance 27.38 USD Expenses:Health:Vision:Insurance 42.30 USD Expenses:Taxes:Y2015:US:Medicare 106.62 USD Expenses:Taxes:Y2015:US:Federal 1062.92 USD Expenses:Taxes:Y2015:US:State 365.08 USD Expenses:Taxes:Y2015:US:CityNYC 174.92 USD Expenses:Taxes:Y2015:US:SDI 1.12 USD Expenses:Taxes:Y2015:US:SocSec 281.54 USD Assets:US:Federal:PreTax401k 0.00 IRAUSD Assets:US:Hooli:Vacation 5 VACHR Income:US:Hooli:Vacation -5 VACHR * Taxes 1980-05-12 open Income:US:Federal:PreTax401k IRAUSD 1980-05-12 open Assets:US:Federal:PreTax401k IRAUSD ** Tax Year 2008 2008-01-01 open Expenses:Taxes:Y2008:US:Federal:PreTax401k IRAUSD 2008-01-01 open Expenses:Taxes:Y2008:US:Medicare USD 2008-01-01 open Expenses:Taxes:Y2008:US:Federal USD 2008-01-01 open Expenses:Taxes:Y2008:US:CityNYC USD 2008-01-01 open Expenses:Taxes:Y2008:US:SDI USD 2008-01-01 open Expenses:Taxes:Y2008:US:State USD 2008-01-01 open Expenses:Taxes:Y2008:US:SocSec USD 2008-01-01 balance Assets:US:Federal:PreTax401k 0 IRAUSD 2008-01-01 * "Allowed contributions for one year" Income:US:Federal:PreTax401k -15500 IRAUSD Assets:US:Federal:PreTax401k 15500 IRAUSD 2009-03-25 * "Filing taxes for 2008" Expenses:Taxes:Y2008:US:Federal 500.18 USD Expenses:Taxes:Y2008:US:State 400.31 USD Liabilities:AccountsPayable -900.49 USD 2009-03-28 * "FEDERAL TAXPYMT" Assets:US:BofA:Checking -500.18 USD Liabilities:AccountsPayable 500.18 USD 2009-03-29 * "STATE TAX & FINANC PYMT" Assets:US:BofA:Checking -400.31 USD Liabilities:AccountsPayable 400.31 USD ** Tax Year 2009 2009-01-01 open Expenses:Taxes:Y2009:US:Federal:PreTax401k IRAUSD 2009-01-01 open Expenses:Taxes:Y2009:US:Medicare USD 2009-01-01 open Expenses:Taxes:Y2009:US:Federal USD 2009-01-01 open Expenses:Taxes:Y2009:US:CityNYC USD 2009-01-01 open Expenses:Taxes:Y2009:US:SDI USD 2009-01-01 open Expenses:Taxes:Y2009:US:State USD 2009-01-01 open Expenses:Taxes:Y2009:US:SocSec USD 2009-01-01 balance Assets:US:Federal:PreTax401k 0 IRAUSD 2009-01-01 * "Allowed contributions for one year" Income:US:Federal:PreTax401k -16500 IRAUSD Assets:US:Federal:PreTax401k 16500 IRAUSD 2010-03-20 * "Filing taxes for 2009" Expenses:Taxes:Y2009:US:Federal 381.58 USD Expenses:Taxes:Y2009:US:State 126.49 USD Liabilities:AccountsPayable -508.07 USD 2010-03-20 * "STATE TAX & FINANC PYMT" Assets:US:BofA:Checking -126.49 USD Liabilities:AccountsPayable 126.49 USD 2010-03-21 * "FEDERAL TAXPYMT" Assets:US:BofA:Checking -381.58 USD Liabilities:AccountsPayable 381.58 USD ** Tax Year 2010 2010-01-01 open Expenses:Taxes:Y2010:US:Federal:PreTax401k IRAUSD 2010-01-01 open Expenses:Taxes:Y2010:US:Medicare USD 2010-01-01 open Expenses:Taxes:Y2010:US:Federal USD 2010-01-01 open Expenses:Taxes:Y2010:US:CityNYC USD 2010-01-01 open Expenses:Taxes:Y2010:US:SDI USD 2010-01-01 open Expenses:Taxes:Y2010:US:State USD 2010-01-01 open Expenses:Taxes:Y2010:US:SocSec USD 2010-01-01 balance Assets:US:Federal:PreTax401k 0 IRAUSD 2010-01-01 * "Allowed contributions for one year" Income:US:Federal:PreTax401k -16500 IRAUSD Assets:US:Federal:PreTax401k 16500 IRAUSD 2011-03-21 * "Filing taxes for 2010" Expenses:Taxes:Y2010:US:Federal 638.74 USD Expenses:Taxes:Y2010:US:State 105.40 USD Liabilities:AccountsPayable -744.14 USD 2011-03-21 * "FEDERAL TAXPYMT" Assets:US:BofA:Checking -638.74 USD Liabilities:AccountsPayable 638.74 USD 2011-03-23 * "STATE TAX & FINANC PYMT" Assets:US:BofA:Checking -105.40 USD Liabilities:AccountsPayable 105.40 USD ** Tax Year 2011 2011-01-01 open Expenses:Taxes:Y2011:US:Federal:PreTax401k IRAUSD 2011-01-01 open Expenses:Taxes:Y2011:US:Medicare USD 2011-01-01 open Expenses:Taxes:Y2011:US:Federal USD 2011-01-01 open Expenses:Taxes:Y2011:US:CityNYC USD 2011-01-01 open Expenses:Taxes:Y2011:US:SDI USD 2011-01-01 open Expenses:Taxes:Y2011:US:State USD 2011-01-01 open Expenses:Taxes:Y2011:US:SocSec USD 2011-01-01 balance Assets:US:Federal:PreTax401k 0 IRAUSD 2011-01-01 * "Allowed contributions for one year" Income:US:Federal:PreTax401k -16500 IRAUSD Assets:US:Federal:PreTax401k 16500 IRAUSD 2012-03-23 * "Filing taxes for 2011" Expenses:Taxes:Y2011:US:Federal 109.84 USD Expenses:Taxes:Y2011:US:State 157.09 USD Liabilities:AccountsPayable -266.93 USD 2012-03-23 * "STATE TAX & FINANC PYMT" Assets:US:BofA:Checking -157.09 USD Liabilities:AccountsPayable 157.09 USD 2012-03-25 * "FEDERAL TAXPYMT" Assets:US:BofA:Checking -109.84 USD Liabilities:AccountsPayable 109.84 USD ** Tax Year 2012 2012-01-01 open Expenses:Taxes:Y2012:US:Federal:PreTax401k IRAUSD 2012-01-01 open Expenses:Taxes:Y2012:US:Medicare USD 2012-01-01 open Expenses:Taxes:Y2012:US:Federal USD 2012-01-01 open Expenses:Taxes:Y2012:US:CityNYC USD 2012-01-01 open Expenses:Taxes:Y2012:US:SDI USD 2012-01-01 open Expenses:Taxes:Y2012:US:State USD 2012-01-01 open Expenses:Taxes:Y2012:US:SocSec USD 2012-01-01 balance Assets:US:Federal:PreTax401k 0 IRAUSD 2012-01-01 * "Allowed contributions for one year" Income:US:Federal:PreTax401k -17000 IRAUSD Assets:US:Federal:PreTax401k 17000 IRAUSD 2013-03-21 * "Filing taxes for 2012" Expenses:Taxes:Y2012:US:Federal 375.41 USD Expenses:Taxes:Y2012:US:State 347.88 USD Liabilities:AccountsPayable -723.29 USD 2013-03-21 * "STATE TAX & FINANC PYMT" Assets:US:BofA:Checking -347.88 USD Liabilities:AccountsPayable 347.88 USD 2013-03-22 * "FEDERAL TAXPYMT" Assets:US:BofA:Checking -375.41 USD Liabilities:AccountsPayable 375.41 USD ** Tax Year 2013 2013-01-01 open Expenses:Taxes:Y2013:US:Federal:PreTax401k IRAUSD 2013-01-01 open Expenses:Taxes:Y2013:US:Medicare USD 2013-01-01 open Expenses:Taxes:Y2013:US:Federal USD 2013-01-01 open Expenses:Taxes:Y2013:US:CityNYC USD 2013-01-01 open Expenses:Taxes:Y2013:US:SDI USD 2013-01-01 open Expenses:Taxes:Y2013:US:State USD 2013-01-01 open Expenses:Taxes:Y2013:US:SocSec USD 2013-01-01 balance Assets:US:Federal:PreTax401k 0 IRAUSD 2013-01-01 * "Allowed contributions for one year" Income:US:Federal:PreTax401k -17500 IRAUSD Assets:US:Federal:PreTax401k 17500 IRAUSD 2014-03-23 * "Filing taxes for 2013" Expenses:Taxes:Y2013:US:Federal 486.10 USD Expenses:Taxes:Y2013:US:State 300.56 USD Liabilities:AccountsPayable -786.66 USD 2014-03-24 * "STATE TAX & FINANC PYMT" Assets:US:BofA:Checking -300.56 USD Liabilities:AccountsPayable 300.56 USD 2014-03-27 * "FEDERAL TAXPYMT" Assets:US:BofA:Checking -486.10 USD Liabilities:AccountsPayable 486.10 USD ** Tax Year 2014 2014-01-01 open Expenses:Taxes:Y2014:US:Federal:PreTax401k IRAUSD 2014-01-01 open Expenses:Taxes:Y2014:US:Medicare USD 2014-01-01 open Expenses:Taxes:Y2014:US:Federal USD 2014-01-01 open Expenses:Taxes:Y2014:US:CityNYC USD 2014-01-01 open Expenses:Taxes:Y2014:US:SDI USD 2014-01-01 open Expenses:Taxes:Y2014:US:State USD 2014-01-01 open Expenses:Taxes:Y2014:US:SocSec USD 2014-01-01 balance Assets:US:Federal:PreTax401k 0 IRAUSD 2014-01-01 * "Allowed contributions for one year" Income:US:Federal:PreTax401k -17500 IRAUSD Assets:US:Federal:PreTax401k 17500 IRAUSD 2015-03-23 * "Filing taxes for 2014" Expenses:Taxes:Y2014:US:Federal 589.32 USD Expenses:Taxes:Y2014:US:State 312.40 USD Liabilities:AccountsPayable -901.72 USD 2015-03-25 * "STATE TAX & FINANC PYMT" Assets:US:BofA:Checking -312.40 USD Liabilities:AccountsPayable 312.40 USD 2015-03-27 * "FEDERAL TAXPYMT" Assets:US:BofA:Checking -589.32 USD Liabilities:AccountsPayable 589.32 USD ** Tax Year 2015 2015-01-01 open Expenses:Taxes:Y2015:US:Federal:PreTax401k IRAUSD 2015-01-01 open Expenses:Taxes:Y2015:US:Medicare USD 2015-01-01 open Expenses:Taxes:Y2015:US:Federal USD 2015-01-01 open Expenses:Taxes:Y2015:US:CityNYC USD 2015-01-01 open Expenses:Taxes:Y2015:US:SDI USD 2015-01-01 open Expenses:Taxes:Y2015:US:State USD 2015-01-01 open Expenses:Taxes:Y2015:US:SocSec USD 2015-01-01 balance Assets:US:Federal:PreTax401k 0 IRAUSD 2015-01-01 * "Allowed contributions for one year" Income:US:Federal:PreTax401k -18000 IRAUSD Assets:US:Federal:PreTax401k 18000 IRAUSD * Expenses 1980-05-12 open Expenses:Food:Groceries 1980-05-12 open Expenses:Food:Restaurant 1980-05-12 open Expenses:Food:Coffee 1980-05-12 open Expenses:Food:Alcohol 1980-05-12 open Expenses:Transport:Tram 1980-05-12 open Expenses:Home:Rent 1980-05-12 open Expenses:Home:Electricity 1980-05-12 open Expenses:Home:Internet 1980-05-12 open Expenses:Home:Phone 1980-05-12 open Expenses:Financial:Fees 1980-05-12 open Expenses:Financial:Commissions * Prices 2008-01-04 price VBMPX 108.61 USD 2008-01-04 price RGAGX 155.99 USD 2008-01-04 price ITOT 60.56 USD 2008-01-04 price VEA 92.07 USD 2008-01-04 price VHT 50.75 USD 2008-01-04 price GLD 58.08 USD 2008-01-11 price VBMPX 108.12 USD 2008-01-11 price RGAGX 158.22 USD 2008-01-11 price ITOT 63.27 USD 2008-01-11 price VEA 92.72 USD 2008-01-11 price VHT 50.74 USD 2008-01-11 price GLD 56.85 USD 2008-01-18 price VBMPX 108.07 USD 2008-01-18 price RGAGX 160.08 USD 2008-01-18 price ITOT 63.44 USD 2008-01-18 price VEA 92.34 USD 2008-01-18 price VHT 52.39 USD 2008-01-18 price GLD 58.64 USD 2008-01-25 price VBMPX 110.94 USD 2008-01-25 price RGAGX 164.50 USD 2008-01-25 price ITOT 65.83 USD 2008-01-25 price VEA 91.82 USD 2008-01-25 price VHT 52.96 USD 2008-01-25 price GLD 57.44 USD 2008-02-01 price VBMPX 112.63 USD 2008-02-01 price RGAGX 162.42 USD 2008-02-01 price ITOT 65.57 USD 2008-02-01 price VEA 91.44 USD 2008-02-01 price VHT 54.01 USD 2008-02-01 price GLD 59.17 USD 2008-02-08 price VBMPX 115.64 USD 2008-02-08 price RGAGX 161.50 USD 2008-02-08 price ITOT 64.13 USD 2008-02-08 price VEA 92.58 USD 2008-02-08 price VHT 54.23 USD 2008-02-08 price GLD 60.31 USD 2008-02-15 price VBMPX 119.43 USD 2008-02-15 price RGAGX 164.78 USD 2008-02-15 price ITOT 64.27 USD 2008-02-15 price VEA 92.92 USD 2008-02-15 price VHT 54.34 USD 2008-02-15 price GLD 60.00 USD 2008-02-22 price VBMPX 118.21 USD 2008-02-22 price RGAGX 163.70 USD 2008-02-22 price ITOT 64.49 USD 2008-02-22 price VEA 94.13 USD 2008-02-22 price VHT 54.34 USD 2008-02-22 price GLD 58.22 USD 2008-02-29 price VBMPX 120.58 USD 2008-02-29 price RGAGX 158.41 USD 2008-02-29 price ITOT 64.46 USD 2008-02-29 price VEA 94.27 USD 2008-02-29 price VHT 53.43 USD 2008-02-29 price GLD 58.16 USD 2008-03-07 price VBMPX 122.56 USD 2008-03-07 price RGAGX 156.84 USD 2008-03-07 price ITOT 64.70 USD 2008-03-07 price VEA 94.47 USD 2008-03-07 price VHT 53.33 USD 2008-03-07 price GLD 59.25 USD 2008-03-14 price VBMPX 120.51 USD 2008-03-14 price RGAGX 157.13 USD 2008-03-14 price ITOT 64.75 USD 2008-03-14 price VEA 94.35 USD 2008-03-14 price VHT 55.27 USD 2008-03-14 price GLD 61.38 USD 2008-03-21 price VBMPX 121.27 USD 2008-03-21 price RGAGX 159.07 USD 2008-03-21 price ITOT 64.49 USD 2008-03-21 price VEA 93.23 USD 2008-03-21 price VHT 55.84 USD 2008-03-21 price GLD 61.62 USD 2008-03-28 price VBMPX 119.26 USD 2008-03-28 price RGAGX 159.60 USD 2008-03-28 price ITOT 65.20 USD 2008-03-28 price VEA 93.55 USD 2008-03-28 price VHT 55.22 USD 2008-03-28 price GLD 61.03 USD 2008-04-04 price VBMPX 124.56 USD 2008-04-04 price RGAGX 160.30 USD 2008-04-04 price ITOT 66.50 USD 2008-04-04 price VEA 93.78 USD 2008-04-04 price VHT 55.57 USD 2008-04-04 price GLD 61.13 USD 2008-04-11 price VBMPX 126.33 USD 2008-04-11 price RGAGX 163.04 USD 2008-04-11 price ITOT 65.81 USD 2008-04-11 price VEA 92.61 USD 2008-04-11 price VHT 55.46 USD 2008-04-11 price GLD 61.68 USD 2008-04-18 price VBMPX 121.18 USD 2008-04-18 price RGAGX 163.01 USD 2008-04-18 price ITOT 65.00 USD 2008-04-18 price VEA 93.10 USD 2008-04-18 price VHT 56.50 USD 2008-04-18 price GLD 60.74 USD 2008-04-25 price VBMPX 121.80 USD 2008-04-25 price RGAGX 165.12 USD 2008-04-25 price ITOT 65.90 USD 2008-04-25 price VEA 93.39 USD 2008-04-25 price VHT 57.47 USD 2008-04-25 price GLD 59.90 USD 2008-05-02 price VBMPX 124.04 USD 2008-05-02 price RGAGX 166.19 USD 2008-05-02 price ITOT 65.61 USD 2008-05-02 price VEA 92.59 USD 2008-05-02 price VHT 58.08 USD 2008-05-02 price GLD 58.38 USD 2008-05-09 price VBMPX 124.90 USD 2008-05-09 price RGAGX 166.95 USD 2008-05-09 price ITOT 64.40 USD 2008-05-09 price VEA 94.35 USD 2008-05-09 price VHT 56.53 USD 2008-05-09 price GLD 58.80 USD 2008-05-16 price VBMPX 122.78 USD 2008-05-16 price RGAGX 168.08 USD 2008-05-16 price ITOT 64.69 USD 2008-05-16 price VEA 94.36 USD 2008-05-16 price VHT 56.58 USD 2008-05-16 price GLD 59.88 USD 2008-05-23 price VBMPX 121.72 USD 2008-05-23 price RGAGX 170.16 USD 2008-05-23 price ITOT 63.80 USD 2008-05-23 price VEA 94.71 USD 2008-05-23 price VHT 56.54 USD 2008-05-23 price GLD 59.13 USD 2008-05-30 price VBMPX 119.82 USD 2008-05-30 price RGAGX 173.01 USD 2008-05-30 price ITOT 64.00 USD 2008-05-30 price VEA 95.13 USD 2008-05-30 price VHT 56.41 USD 2008-05-30 price GLD 59.26 USD 2008-06-06 price VBMPX 122.42 USD 2008-06-06 price RGAGX 174.50 USD 2008-06-06 price ITOT 64.19 USD 2008-06-06 price VEA 95.03 USD 2008-06-06 price VHT 56.65 USD 2008-06-06 price GLD 60.79 USD 2008-06-13 price VBMPX 118.02 USD 2008-06-13 price RGAGX 173.71 USD 2008-06-13 price ITOT 65.23 USD 2008-06-13 price VEA 94.85 USD 2008-06-13 price VHT 57.74 USD 2008-06-13 price GLD 62.00 USD 2008-06-20 price VBMPX 122.18 USD 2008-06-20 price RGAGX 173.44 USD 2008-06-20 price ITOT 65.80 USD 2008-06-20 price VEA 95.80 USD 2008-06-20 price VHT 56.35 USD 2008-06-20 price GLD 62.03 USD 2008-06-27 price VBMPX 122.00 USD 2008-06-27 price RGAGX 174.31 USD 2008-06-27 price ITOT 66.62 USD 2008-06-27 price VEA 95.33 USD 2008-06-27 price VHT 57.65 USD 2008-06-27 price GLD 61.23 USD 2008-07-04 price VBMPX 124.50 USD 2008-07-04 price RGAGX 172.14 USD 2008-07-04 price ITOT 66.49 USD 2008-07-04 price VEA 95.88 USD 2008-07-04 price VHT 57.51 USD 2008-07-04 price GLD 60.04 USD 2008-07-11 price VBMPX 121.52 USD 2008-07-11 price RGAGX 169.58 USD 2008-07-11 price ITOT 67.49 USD 2008-07-11 price VEA 96.14 USD 2008-07-11 price VHT 57.88 USD 2008-07-11 price GLD 57.99 USD 2008-07-18 price VBMPX 123.83 USD 2008-07-18 price RGAGX 171.77 USD 2008-07-18 price ITOT 69.52 USD 2008-07-18 price VEA 97.54 USD 2008-07-18 price VHT 59.26 USD 2008-07-18 price GLD 58.19 USD 2008-07-25 price VBMPX 121.90 USD 2008-07-25 price RGAGX 175.10 USD 2008-07-25 price ITOT 69.00 USD 2008-07-25 price VEA 95.95 USD 2008-07-25 price VHT 57.80 USD 2008-07-25 price GLD 58.85 USD 2008-08-01 price VBMPX 122.38 USD 2008-08-01 price RGAGX 178.10 USD 2008-08-01 price ITOT 69.24 USD 2008-08-01 price VEA 96.60 USD 2008-08-01 price VHT 56.81 USD 2008-08-01 price GLD 59.15 USD 2008-08-08 price VBMPX 128.08 USD 2008-08-08 price RGAGX 182.55 USD 2008-08-08 price ITOT 66.86 USD 2008-08-08 price VEA 96.73 USD 2008-08-08 price VHT 57.74 USD 2008-08-08 price GLD 60.67 USD 2008-08-15 price VBMPX 129.63 USD 2008-08-15 price RGAGX 181.97 USD 2008-08-15 price ITOT 66.73 USD 2008-08-15 price VEA 95.72 USD 2008-08-15 price VHT 58.06 USD 2008-08-15 price GLD 60.49 USD 2008-08-22 price VBMPX 128.90 USD 2008-08-22 price RGAGX 181.69 USD 2008-08-22 price ITOT 67.77 USD 2008-08-22 price VEA 95.37 USD 2008-08-22 price VHT 56.82 USD 2008-08-22 price GLD 61.66 USD 2008-08-29 price VBMPX 131.50 USD 2008-08-29 price RGAGX 181.59 USD 2008-08-29 price ITOT 68.64 USD 2008-08-29 price VEA 96.15 USD 2008-08-29 price VHT 58.13 USD 2008-08-29 price GLD 60.25 USD 2008-09-05 price VBMPX 127.05 USD 2008-09-05 price RGAGX 183.35 USD 2008-09-05 price ITOT 68.42 USD 2008-09-05 price VEA 95.58 USD 2008-09-05 price VHT 58.82 USD 2008-09-05 price GLD 62.70 USD 2008-09-12 price VBMPX 130.57 USD 2008-09-12 price RGAGX 186.48 USD 2008-09-12 price ITOT 67.57 USD 2008-09-12 price VEA 95.07 USD 2008-09-12 price VHT 59.80 USD 2008-09-12 price GLD 61.68 USD 2008-09-19 price VBMPX 131.34 USD 2008-09-19 price RGAGX 187.68 USD 2008-09-19 price ITOT 68.53 USD 2008-09-19 price VEA 95.62 USD 2008-09-19 price VHT 60.53 USD 2008-09-19 price GLD 63.50 USD 2008-09-26 price VBMPX 139.08 USD 2008-09-26 price RGAGX 188.25 USD 2008-09-26 price ITOT 70.79 USD 2008-09-26 price VEA 96.77 USD 2008-09-26 price VHT 61.31 USD 2008-09-26 price GLD 63.59 USD 2008-10-03 price VBMPX 137.75 USD 2008-10-03 price RGAGX 187.48 USD 2008-10-03 price ITOT 71.54 USD 2008-10-03 price VEA 96.16 USD 2008-10-03 price VHT 60.78 USD 2008-10-03 price GLD 63.12 USD 2008-10-10 price VBMPX 133.94 USD 2008-10-10 price RGAGX 188.71 USD 2008-10-10 price ITOT 71.16 USD 2008-10-10 price VEA 95.31 USD 2008-10-10 price VHT 61.09 USD 2008-10-10 price GLD 64.27 USD 2008-10-17 price VBMPX 132.54 USD 2008-10-17 price RGAGX 187.08 USD 2008-10-17 price ITOT 72.62 USD 2008-10-17 price VEA 96.49 USD 2008-10-17 price VHT 62.44 USD 2008-10-17 price GLD 65.30 USD 2008-10-24 price VBMPX 131.52 USD 2008-10-24 price RGAGX 186.47 USD 2008-10-24 price ITOT 72.24 USD 2008-10-24 price VEA 97.02 USD 2008-10-24 price VHT 62.74 USD 2008-10-24 price GLD 64.06 USD 2008-10-31 price VBMPX 130.06 USD 2008-10-31 price RGAGX 188.61 USD 2008-10-31 price ITOT 73.32 USD 2008-10-31 price VEA 96.63 USD 2008-10-31 price VHT 63.33 USD 2008-10-31 price GLD 64.69 USD 2008-11-07 price VBMPX 127.81 USD 2008-11-07 price RGAGX 187.64 USD 2008-11-07 price ITOT 74.99 USD 2008-11-07 price VEA 97.39 USD 2008-11-07 price VHT 62.72 USD 2008-11-07 price GLD 67.05 USD 2008-11-14 price VBMPX 125.57 USD 2008-11-14 price RGAGX 184.53 USD 2008-11-14 price ITOT 72.52 USD 2008-11-14 price VEA 97.22 USD 2008-11-14 price VHT 63.84 USD 2008-11-14 price GLD 67.88 USD 2008-11-21 price VBMPX 127.76 USD 2008-11-21 price RGAGX 182.27 USD 2008-11-21 price ITOT 72.48 USD 2008-11-21 price VEA 97.34 USD 2008-11-21 price VHT 65.24 USD 2008-11-21 price GLD 67.17 USD 2008-11-28 price VBMPX 123.70 USD 2008-11-28 price RGAGX 184.37 USD 2008-11-28 price ITOT 73.11 USD 2008-11-28 price VEA 96.20 USD 2008-11-28 price VHT 64.48 USD 2008-11-28 price GLD 68.25 USD 2008-12-05 price VBMPX 124.45 USD 2008-12-05 price RGAGX 184.65 USD 2008-12-05 price ITOT 72.56 USD 2008-12-05 price VEA 96.37 USD 2008-12-05 price VHT 64.24 USD 2008-12-05 price GLD 68.76 USD 2008-12-12 price VBMPX 123.31 USD 2008-12-12 price RGAGX 183.11 USD 2008-12-12 price ITOT 74.13 USD 2008-12-12 price VEA 95.85 USD 2008-12-12 price VHT 66.11 USD 2008-12-12 price GLD 70.22 USD 2008-12-19 price VBMPX 120.23 USD 2008-12-19 price RGAGX 183.57 USD 2008-12-19 price ITOT 73.45 USD 2008-12-19 price VEA 96.67 USD 2008-12-19 price VHT 68.16 USD 2008-12-19 price GLD 69.63 USD 2008-12-26 price VBMPX 120.99 USD 2008-12-26 price RGAGX 185.11 USD 2008-12-26 price ITOT 74.50 USD 2008-12-26 price VEA 96.34 USD 2008-12-26 price VHT 68.57 USD 2008-12-26 price GLD 67.23 USD 2009-01-02 price VBMPX 123.78 USD 2009-01-02 price RGAGX 186.67 USD 2009-01-02 price ITOT 74.50 USD 2009-01-02 price VEA 97.53 USD 2009-01-02 price VHT 69.52 USD 2009-01-02 price GLD 66.81 USD 2009-01-09 price VBMPX 125.20 USD 2009-01-09 price RGAGX 186.80 USD 2009-01-09 price ITOT 72.58 USD 2009-01-09 price VEA 97.95 USD 2009-01-09 price VHT 68.39 USD 2009-01-09 price GLD 69.99 USD 2009-01-16 price VBMPX 124.73 USD 2009-01-16 price RGAGX 186.03 USD 2009-01-16 price ITOT 71.37 USD 2009-01-16 price VEA 98.63 USD 2009-01-16 price VHT 66.79 USD 2009-01-16 price GLD 69.30 USD 2009-01-23 price VBMPX 122.49 USD 2009-01-23 price RGAGX 188.83 USD 2009-01-23 price ITOT 71.20 USD 2009-01-23 price VEA 98.94 USD 2009-01-23 price VHT 65.21 USD 2009-01-23 price GLD 68.39 USD 2009-01-30 price VBMPX 123.40 USD 2009-01-30 price RGAGX 190.55 USD 2009-01-30 price ITOT 70.87 USD 2009-01-30 price VEA 99.36 USD 2009-01-30 price VHT 65.73 USD 2009-01-30 price GLD 69.31 USD 2009-02-06 price VBMPX 122.53 USD 2009-02-06 price RGAGX 190.09 USD 2009-02-06 price ITOT 70.69 USD 2009-02-06 price VEA 99.03 USD 2009-02-06 price VHT 65.39 USD 2009-02-06 price GLD 70.99 USD 2009-02-13 price VBMPX 124.94 USD 2009-02-13 price RGAGX 194.81 USD 2009-02-13 price ITOT 70.96 USD 2009-02-13 price VEA 99.29 USD 2009-02-13 price VHT 65.03 USD 2009-02-13 price GLD 70.42 USD 2009-02-20 price VBMPX 125.70 USD 2009-02-20 price RGAGX 196.86 USD 2009-02-20 price ITOT 70.30 USD 2009-02-20 price VEA 100.90 USD 2009-02-20 price VHT 64.00 USD 2009-02-20 price GLD 67.71 USD 2009-02-27 price VBMPX 125.55 USD 2009-02-27 price RGAGX 198.86 USD 2009-02-27 price ITOT 70.03 USD 2009-02-27 price VEA 100.73 USD 2009-02-27 price VHT 64.98 USD 2009-02-27 price GLD 67.86 USD 2009-03-06 price VBMPX 128.66 USD 2009-03-06 price RGAGX 200.23 USD 2009-03-06 price ITOT 70.22 USD 2009-03-06 price VEA 100.86 USD 2009-03-06 price VHT 65.07 USD 2009-03-06 price GLD 68.05 USD 2009-03-13 price VBMPX 128.01 USD 2009-03-13 price RGAGX 204.25 USD 2009-03-13 price ITOT 72.28 USD 2009-03-13 price VEA 101.22 USD 2009-03-13 price VHT 65.81 USD 2009-03-13 price GLD 68.92 USD 2009-03-20 price VBMPX 129.44 USD 2009-03-20 price RGAGX 201.09 USD 2009-03-20 price ITOT 72.62 USD 2009-03-20 price VEA 101.24 USD 2009-03-20 price VHT 66.70 USD 2009-03-20 price GLD 68.00 USD 2009-03-27 price VBMPX 128.54 USD 2009-03-27 price RGAGX 203.99 USD 2009-03-27 price ITOT 73.63 USD 2009-03-27 price VEA 101.60 USD 2009-03-27 price VHT 69.69 USD 2009-03-27 price GLD 68.45 USD 2009-04-03 price VBMPX 128.32 USD 2009-04-03 price RGAGX 203.24 USD 2009-04-03 price ITOT 73.75 USD 2009-04-03 price VEA 100.00 USD 2009-04-03 price VHT 67.74 USD 2009-04-03 price GLD 68.32 USD 2009-04-10 price VBMPX 130.11 USD 2009-04-10 price RGAGX 204.75 USD 2009-04-10 price ITOT 73.03 USD 2009-04-10 price VEA 100.12 USD 2009-04-10 price VHT 67.45 USD 2009-04-10 price GLD 66.96 USD 2009-04-17 price VBMPX 127.46 USD 2009-04-17 price RGAGX 205.82 USD 2009-04-17 price ITOT 73.59 USD 2009-04-17 price VEA 99.45 USD 2009-04-17 price VHT 67.41 USD 2009-04-17 price GLD 65.85 USD 2009-04-24 price VBMPX 128.01 USD 2009-04-24 price RGAGX 206.18 USD 2009-04-24 price ITOT 72.35 USD 2009-04-24 price VEA 101.10 USD 2009-04-24 price VHT 67.58 USD 2009-04-24 price GLD 66.23 USD 2009-05-01 price VBMPX 128.45 USD 2009-05-01 price RGAGX 205.50 USD 2009-05-01 price ITOT 70.48 USD 2009-05-01 price VEA 102.55 USD 2009-05-01 price VHT 67.58 USD 2009-05-01 price GLD 63.09 USD 2009-05-08 price VBMPX 128.62 USD 2009-05-08 price RGAGX 210.49 USD 2009-05-08 price ITOT 69.74 USD 2009-05-08 price VEA 104.03 USD 2009-05-08 price VHT 67.13 USD 2009-05-08 price GLD 64.06 USD 2009-05-15 price VBMPX 133.31 USD 2009-05-15 price RGAGX 208.22 USD 2009-05-15 price ITOT 69.06 USD 2009-05-15 price VEA 104.19 USD 2009-05-15 price VHT 67.87 USD 2009-05-15 price GLD 65.13 USD 2009-05-22 price VBMPX 134.30 USD 2009-05-22 price RGAGX 206.65 USD 2009-05-22 price ITOT 68.50 USD 2009-05-22 price VEA 103.78 USD 2009-05-22 price VHT 67.69 USD 2009-05-22 price GLD 66.30 USD 2009-05-29 price VBMPX 134.56 USD 2009-05-29 price RGAGX 207.45 USD 2009-05-29 price ITOT 69.17 USD 2009-05-29 price VEA 103.32 USD 2009-05-29 price VHT 68.18 USD 2009-05-29 price GLD 65.00 USD 2009-06-05 price VBMPX 137.73 USD 2009-06-05 price RGAGX 209.87 USD 2009-06-05 price ITOT 67.69 USD 2009-06-05 price VEA 102.99 USD 2009-06-05 price VHT 68.00 USD 2009-06-05 price GLD 64.15 USD 2009-06-12 price VBMPX 136.68 USD 2009-06-12 price RGAGX 209.25 USD 2009-06-12 price ITOT 68.16 USD 2009-06-12 price VEA 103.44 USD 2009-06-12 price VHT 69.44 USD 2009-06-12 price GLD 63.17 USD 2009-06-19 price VBMPX 137.94 USD 2009-06-19 price RGAGX 214.22 USD 2009-06-19 price ITOT 68.39 USD 2009-06-19 price VEA 104.55 USD 2009-06-19 price VHT 68.03 USD 2009-06-19 price GLD 62.85 USD 2009-06-26 price VBMPX 139.25 USD 2009-06-26 price RGAGX 217.41 USD 2009-06-26 price ITOT 67.12 USD 2009-06-26 price VEA 105.00 USD 2009-06-26 price VHT 68.45 USD 2009-06-26 price GLD 64.12 USD 2009-07-03 price VBMPX 142.05 USD 2009-07-03 price RGAGX 218.53 USD 2009-07-03 price ITOT 68.78 USD 2009-07-03 price VEA 105.71 USD 2009-07-03 price VHT 68.81 USD 2009-07-03 price GLD 62.90 USD 2009-07-10 price VBMPX 141.74 USD 2009-07-10 price RGAGX 222.63 USD 2009-07-10 price ITOT 70.00 USD 2009-07-10 price VEA 107.56 USD 2009-07-10 price VHT 67.81 USD 2009-07-10 price GLD 64.01 USD 2009-07-17 price VBMPX 142.72 USD 2009-07-17 price RGAGX 224.77 USD 2009-07-17 price ITOT 70.17 USD 2009-07-17 price VEA 108.57 USD 2009-07-17 price VHT 65.27 USD 2009-07-17 price GLD 63.57 USD 2009-07-24 price VBMPX 139.46 USD 2009-07-24 price RGAGX 226.67 USD 2009-07-24 price ITOT 70.42 USD 2009-07-24 price VEA 109.38 USD 2009-07-24 price VHT 66.49 USD 2009-07-24 price GLD 63.86 USD 2009-07-31 price VBMPX 140.77 USD 2009-07-31 price RGAGX 229.48 USD 2009-07-31 price ITOT 70.67 USD 2009-07-31 price VEA 110.44 USD 2009-07-31 price VHT 66.10 USD 2009-07-31 price GLD 65.51 USD 2009-08-07 price VBMPX 142.50 USD 2009-08-07 price RGAGX 234.85 USD 2009-08-07 price ITOT 70.29 USD 2009-08-07 price VEA 110.53 USD 2009-08-07 price VHT 64.84 USD 2009-08-07 price GLD 65.64 USD 2009-08-14 price VBMPX 141.66 USD 2009-08-14 price RGAGX 237.96 USD 2009-08-14 price ITOT 70.23 USD 2009-08-14 price VEA 111.79 USD 2009-08-14 price VHT 66.10 USD 2009-08-14 price GLD 66.28 USD 2009-08-21 price VBMPX 139.17 USD 2009-08-21 price RGAGX 238.15 USD 2009-08-21 price ITOT 72.22 USD 2009-08-21 price VEA 111.73 USD 2009-08-21 price VHT 64.51 USD 2009-08-21 price GLD 68.87 USD 2009-08-28 price VBMPX 136.98 USD 2009-08-28 price RGAGX 237.67 USD 2009-08-28 price ITOT 72.66 USD 2009-08-28 price VEA 111.26 USD 2009-08-28 price VHT 65.14 USD 2009-08-28 price GLD 70.02 USD 2009-09-04 price VBMPX 136.21 USD 2009-09-04 price RGAGX 241.77 USD 2009-09-04 price ITOT 70.18 USD 2009-09-04 price VEA 111.95 USD 2009-09-04 price VHT 65.65 USD 2009-09-04 price GLD 72.37 USD 2009-09-11 price VBMPX 136.49 USD 2009-09-11 price RGAGX 238.96 USD 2009-09-11 price ITOT 70.51 USD 2009-09-11 price VEA 113.98 USD 2009-09-11 price VHT 66.14 USD 2009-09-11 price GLD 69.59 USD 2009-09-18 price VBMPX 138.52 USD 2009-09-18 price RGAGX 234.80 USD 2009-09-18 price ITOT 68.86 USD 2009-09-18 price VEA 114.04 USD 2009-09-18 price VHT 66.14 USD 2009-09-18 price GLD 71.48 USD 2009-09-25 price VBMPX 138.69 USD 2009-09-25 price RGAGX 241.10 USD 2009-09-25 price ITOT 70.09 USD 2009-09-25 price VEA 114.58 USD 2009-09-25 price VHT 66.91 USD 2009-09-25 price GLD 71.00 USD 2009-10-02 price VBMPX 136.56 USD 2009-10-02 price RGAGX 244.52 USD 2009-10-02 price ITOT 69.75 USD 2009-10-02 price VEA 115.59 USD 2009-10-02 price VHT 67.05 USD 2009-10-02 price GLD 71.49 USD 2009-10-09 price VBMPX 139.73 USD 2009-10-09 price RGAGX 238.37 USD 2009-10-09 price ITOT 70.17 USD 2009-10-09 price VEA 115.09 USD 2009-10-09 price VHT 67.63 USD 2009-10-09 price GLD 71.00 USD 2009-10-16 price VBMPX 142.13 USD 2009-10-16 price RGAGX 238.70 USD 2009-10-16 price ITOT 69.56 USD 2009-10-16 price VEA 115.29 USD 2009-10-16 price VHT 67.28 USD 2009-10-16 price GLD 70.27 USD 2009-10-23 price VBMPX 139.87 USD 2009-10-23 price RGAGX 240.72 USD 2009-10-23 price ITOT 70.36 USD 2009-10-23 price VEA 115.48 USD 2009-10-23 price VHT 66.56 USD 2009-10-23 price GLD 68.65 USD 2009-10-30 price VBMPX 140.48 USD 2009-10-30 price RGAGX 238.73 USD 2009-10-30 price ITOT 69.53 USD 2009-10-30 price VEA 115.07 USD 2009-10-30 price VHT 67.87 USD 2009-10-30 price GLD 67.62 USD 2009-11-06 price VBMPX 135.93 USD 2009-11-06 price RGAGX 240.29 USD 2009-11-06 price ITOT 69.15 USD 2009-11-06 price VEA 115.44 USD 2009-11-06 price VHT 69.96 USD 2009-11-06 price GLD 67.92 USD 2009-11-13 price VBMPX 137.68 USD 2009-11-13 price RGAGX 239.23 USD 2009-11-13 price ITOT 67.91 USD 2009-11-13 price VEA 117.12 USD 2009-11-13 price VHT 70.95 USD 2009-11-13 price GLD 67.18 USD 2009-11-20 price VBMPX 138.20 USD 2009-11-20 price RGAGX 239.64 USD 2009-11-20 price ITOT 69.24 USD 2009-11-20 price VEA 117.55 USD 2009-11-20 price VHT 71.05 USD 2009-11-20 price GLD 68.19 USD 2009-11-27 price VBMPX 141.48 USD 2009-11-27 price RGAGX 236.71 USD 2009-11-27 price ITOT 69.51 USD 2009-11-27 price VEA 118.70 USD 2009-11-27 price VHT 70.24 USD 2009-11-27 price GLD 68.27 USD 2009-12-04 price VBMPX 141.64 USD 2009-12-04 price RGAGX 237.96 USD 2009-12-04 price ITOT 69.13 USD 2009-12-04 price VEA 118.97 USD 2009-12-04 price VHT 69.50 USD 2009-12-04 price GLD 69.75 USD 2009-12-11 price VBMPX 145.20 USD 2009-12-11 price RGAGX 242.59 USD 2009-12-11 price ITOT 68.63 USD 2009-12-11 price VEA 118.38 USD 2009-12-11 price VHT 68.26 USD 2009-12-11 price GLD 69.56 USD 2009-12-18 price VBMPX 146.63 USD 2009-12-18 price RGAGX 248.48 USD 2009-12-18 price ITOT 69.46 USD 2009-12-18 price VEA 118.23 USD 2009-12-18 price VHT 68.57 USD 2009-12-18 price GLD 69.07 USD 2009-12-25 price VBMPX 146.65 USD 2009-12-25 price RGAGX 253.15 USD 2009-12-25 price ITOT 71.88 USD 2009-12-25 price VEA 118.49 USD 2009-12-25 price VHT 68.64 USD 2009-12-25 price GLD 68.63 USD 2010-01-01 price VBMPX 145.36 USD 2010-01-01 price RGAGX 257.07 USD 2010-01-01 price ITOT 72.34 USD 2010-01-01 price VEA 120.22 USD 2010-01-01 price VHT 67.74 USD 2010-01-01 price GLD 66.86 USD 2010-01-08 price VBMPX 146.23 USD 2010-01-08 price RGAGX 257.93 USD 2010-01-08 price ITOT 71.08 USD 2010-01-08 price VEA 119.88 USD 2010-01-08 price VHT 67.80 USD 2010-01-08 price GLD 64.15 USD 2010-01-15 price VBMPX 144.77 USD 2010-01-15 price RGAGX 261.13 USD 2010-01-15 price ITOT 72.20 USD 2010-01-15 price VEA 120.72 USD 2010-01-15 price VHT 67.23 USD 2010-01-15 price GLD 61.74 USD 2010-01-22 price VBMPX 142.54 USD 2010-01-22 price RGAGX 261.16 USD 2010-01-22 price ITOT 73.07 USD 2010-01-22 price VEA 120.56 USD 2010-01-22 price VHT 68.05 USD 2010-01-22 price GLD 61.21 USD 2010-01-29 price VBMPX 141.86 USD 2010-01-29 price RGAGX 267.45 USD 2010-01-29 price ITOT 73.40 USD 2010-01-29 price VEA 121.58 USD 2010-01-29 price VHT 66.83 USD 2010-01-29 price GLD 62.82 USD 2010-02-05 price VBMPX 144.91 USD 2010-02-05 price RGAGX 263.61 USD 2010-02-05 price ITOT 73.28 USD 2010-02-05 price VEA 122.48 USD 2010-02-05 price VHT 68.48 USD 2010-02-05 price GLD 62.03 USD 2010-02-12 price VBMPX 143.58 USD 2010-02-12 price RGAGX 266.94 USD 2010-02-12 price ITOT 73.09 USD 2010-02-12 price VEA 123.10 USD 2010-02-12 price VHT 69.13 USD 2010-02-12 price GLD 63.42 USD 2010-02-19 price VBMPX 143.92 USD 2010-02-19 price RGAGX 264.41 USD 2010-02-19 price ITOT 72.35 USD 2010-02-19 price VEA 123.38 USD 2010-02-19 price VHT 68.66 USD 2010-02-19 price GLD 62.31 USD 2010-02-26 price VBMPX 143.43 USD 2010-02-26 price RGAGX 260.52 USD 2010-02-26 price ITOT 73.68 USD 2010-02-26 price VEA 124.07 USD 2010-02-26 price VHT 69.08 USD 2010-02-26 price GLD 64.40 USD 2010-03-05 price VBMPX 143.34 USD 2010-03-05 price RGAGX 253.64 USD 2010-03-05 price ITOT 72.40 USD 2010-03-05 price VEA 124.26 USD 2010-03-05 price VHT 70.40 USD 2010-03-05 price GLD 65.21 USD 2010-03-12 price VBMPX 145.57 USD 2010-03-12 price RGAGX 257.24 USD 2010-03-12 price ITOT 71.61 USD 2010-03-12 price VEA 124.84 USD 2010-03-12 price VHT 70.50 USD 2010-03-12 price GLD 64.75 USD 2010-03-19 price VBMPX 144.27 USD 2010-03-19 price RGAGX 256.14 USD 2010-03-19 price ITOT 71.42 USD 2010-03-19 price VEA 126.46 USD 2010-03-19 price VHT 70.66 USD 2010-03-19 price GLD 62.80 USD 2010-03-26 price VBMPX 151.01 USD 2010-03-26 price RGAGX 258.51 USD 2010-03-26 price ITOT 74.48 USD 2010-03-26 price VEA 126.78 USD 2010-03-26 price VHT 70.77 USD 2010-03-26 price GLD 64.67 USD 2010-04-02 price VBMPX 151.18 USD 2010-04-02 price RGAGX 262.87 USD 2010-04-02 price ITOT 73.79 USD 2010-04-02 price VEA 125.52 USD 2010-04-02 price VHT 70.18 USD 2010-04-02 price GLD 63.59 USD 2010-04-09 price VBMPX 150.67 USD 2010-04-09 price RGAGX 266.26 USD 2010-04-09 price ITOT 73.23 USD 2010-04-09 price VEA 123.60 USD 2010-04-09 price VHT 70.26 USD 2010-04-09 price GLD 63.42 USD 2010-04-16 price VBMPX 145.16 USD 2010-04-16 price RGAGX 266.62 USD 2010-04-16 price ITOT 72.26 USD 2010-04-16 price VEA 122.81 USD 2010-04-16 price VHT 70.38 USD 2010-04-16 price GLD 65.35 USD 2010-04-23 price VBMPX 150.15 USD 2010-04-23 price RGAGX 272.20 USD 2010-04-23 price ITOT 72.67 USD 2010-04-23 price VEA 123.76 USD 2010-04-23 price VHT 70.56 USD 2010-04-23 price GLD 67.42 USD 2010-04-30 price VBMPX 151.95 USD 2010-04-30 price RGAGX 274.01 USD 2010-04-30 price ITOT 72.84 USD 2010-04-30 price VEA 123.83 USD 2010-04-30 price VHT 69.81 USD 2010-04-30 price GLD 68.80 USD 2010-05-07 price VBMPX 151.82 USD 2010-05-07 price RGAGX 273.32 USD 2010-05-07 price ITOT 72.37 USD 2010-05-07 price VEA 125.67 USD 2010-05-07 price VHT 68.51 USD 2010-05-07 price GLD 67.45 USD 2010-05-14 price VBMPX 149.87 USD 2010-05-14 price RGAGX 275.55 USD 2010-05-14 price ITOT 73.24 USD 2010-05-14 price VEA 124.93 USD 2010-05-14 price VHT 68.12 USD 2010-05-14 price GLD 69.87 USD 2010-05-21 price VBMPX 151.37 USD 2010-05-21 price RGAGX 277.19 USD 2010-05-21 price ITOT 74.01 USD 2010-05-21 price VEA 124.92 USD 2010-05-21 price VHT 68.96 USD 2010-05-21 price GLD 69.54 USD 2010-05-28 price VBMPX 151.82 USD 2010-05-28 price RGAGX 275.58 USD 2010-05-28 price ITOT 74.30 USD 2010-05-28 price VEA 125.45 USD 2010-05-28 price VHT 69.86 USD 2010-05-28 price GLD 69.12 USD 2010-06-04 price VBMPX 150.41 USD 2010-06-04 price RGAGX 277.11 USD 2010-06-04 price ITOT 73.56 USD 2010-06-04 price VEA 125.19 USD 2010-06-04 price VHT 70.40 USD 2010-06-04 price GLD 67.51 USD 2010-06-11 price VBMPX 153.42 USD 2010-06-11 price RGAGX 277.30 USD 2010-06-11 price ITOT 73.63 USD 2010-06-11 price VEA 126.24 USD 2010-06-11 price VHT 69.82 USD 2010-06-11 price GLD 68.90 USD 2010-06-18 price VBMPX 156.18 USD 2010-06-18 price RGAGX 280.33 USD 2010-06-18 price ITOT 74.95 USD 2010-06-18 price VEA 127.46 USD 2010-06-18 price VHT 71.49 USD 2010-06-18 price GLD 69.69 USD 2010-06-25 price VBMPX 161.12 USD 2010-06-25 price RGAGX 282.69 USD 2010-06-25 price ITOT 73.80 USD 2010-06-25 price VEA 127.96 USD 2010-06-25 price VHT 72.76 USD 2010-06-25 price GLD 70.53 USD 2010-07-02 price VBMPX 162.05 USD 2010-07-02 price RGAGX 287.80 USD 2010-07-02 price ITOT 75.55 USD 2010-07-02 price VEA 127.99 USD 2010-07-02 price VHT 74.26 USD 2010-07-02 price GLD 73.28 USD 2010-07-09 price VBMPX 166.22 USD 2010-07-09 price RGAGX 287.11 USD 2010-07-09 price ITOT 75.35 USD 2010-07-09 price VEA 129.30 USD 2010-07-09 price VHT 74.68 USD 2010-07-09 price GLD 73.94 USD 2010-07-16 price VBMPX 165.81 USD 2010-07-16 price RGAGX 284.63 USD 2010-07-16 price ITOT 76.78 USD 2010-07-16 price VEA 128.07 USD 2010-07-16 price VHT 73.80 USD 2010-07-16 price GLD 76.05 USD 2010-07-23 price VBMPX 166.08 USD 2010-07-23 price RGAGX 291.50 USD 2010-07-23 price ITOT 76.31 USD 2010-07-23 price VEA 128.58 USD 2010-07-23 price VHT 75.50 USD 2010-07-23 price GLD 73.27 USD 2010-07-30 price VBMPX 160.30 USD 2010-07-30 price RGAGX 286.73 USD 2010-07-30 price ITOT 76.92 USD 2010-07-30 price VEA 130.04 USD 2010-07-30 price VHT 75.96 USD 2010-07-30 price GLD 73.23 USD 2010-08-06 price VBMPX 161.95 USD 2010-08-06 price RGAGX 288.11 USD 2010-08-06 price ITOT 76.36 USD 2010-08-06 price VEA 129.11 USD 2010-08-06 price VHT 76.32 USD 2010-08-06 price GLD 75.51 USD 2010-08-13 price VBMPX 161.17 USD 2010-08-13 price RGAGX 292.23 USD 2010-08-13 price ITOT 75.56 USD 2010-08-13 price VEA 128.81 USD 2010-08-13 price VHT 74.53 USD 2010-08-13 price GLD 72.54 USD 2010-08-20 price VBMPX 159.18 USD 2010-08-20 price RGAGX 287.76 USD 2010-08-20 price ITOT 75.75 USD 2010-08-20 price VEA 129.28 USD 2010-08-20 price VHT 76.12 USD 2010-08-20 price GLD 73.50 USD 2010-08-27 price VBMPX 158.40 USD 2010-08-27 price RGAGX 290.16 USD 2010-08-27 price ITOT 75.62 USD 2010-08-27 price VEA 130.16 USD 2010-08-27 price VHT 78.27 USD 2010-08-27 price GLD 76.70 USD 2010-09-03 price VBMPX 166.01 USD 2010-09-03 price RGAGX 291.44 USD 2010-09-03 price ITOT 75.12 USD 2010-09-03 price VEA 130.22 USD 2010-09-03 price VHT 76.50 USD 2010-09-03 price GLD 76.12 USD 2010-09-10 price VBMPX 164.17 USD 2010-09-10 price RGAGX 296.69 USD 2010-09-10 price ITOT 75.24 USD 2010-09-10 price VEA 129.43 USD 2010-09-10 price VHT 77.02 USD 2010-09-10 price GLD 77.13 USD 2010-09-17 price VBMPX 158.88 USD 2010-09-17 price RGAGX 293.58 USD 2010-09-17 price ITOT 75.33 USD 2010-09-17 price VEA 128.67 USD 2010-09-17 price VHT 77.19 USD 2010-09-17 price GLD 76.72 USD 2010-09-24 price VBMPX 161.44 USD 2010-09-24 price RGAGX 293.97 USD 2010-09-24 price ITOT 75.47 USD 2010-09-24 price VEA 131.11 USD 2010-09-24 price VHT 77.16 USD 2010-09-24 price GLD 75.85 USD 2010-10-01 price VBMPX 159.95 USD 2010-10-01 price RGAGX 294.65 USD 2010-10-01 price ITOT 76.68 USD 2010-10-01 price VEA 130.41 USD 2010-10-01 price VHT 76.92 USD 2010-10-01 price GLD 74.94 USD 2010-10-08 price VBMPX 159.31 USD 2010-10-08 price RGAGX 292.90 USD 2010-10-08 price ITOT 77.24 USD 2010-10-08 price VEA 129.24 USD 2010-10-08 price VHT 75.25 USD 2010-10-08 price GLD 76.78 USD 2010-10-15 price VBMPX 160.76 USD 2010-10-15 price RGAGX 297.30 USD 2010-10-15 price ITOT 77.27 USD 2010-10-15 price VEA 129.56 USD 2010-10-15 price VHT 75.42 USD 2010-10-15 price GLD 77.19 USD 2010-10-22 price VBMPX 162.25 USD 2010-10-22 price RGAGX 298.09 USD 2010-10-22 price ITOT 78.40 USD 2010-10-22 price VEA 131.46 USD 2010-10-22 price VHT 77.02 USD 2010-10-22 price GLD 77.64 USD 2010-10-29 price VBMPX 158.17 USD 2010-10-29 price RGAGX 293.09 USD 2010-10-29 price ITOT 78.09 USD 2010-10-29 price VEA 131.24 USD 2010-10-29 price VHT 76.69 USD 2010-10-29 price GLD 76.93 USD 2010-11-05 price VBMPX 158.33 USD 2010-11-05 price RGAGX 292.70 USD 2010-11-05 price ITOT 78.31 USD 2010-11-05 price VEA 131.65 USD 2010-11-05 price VHT 79.04 USD 2010-11-05 price GLD 77.63 USD 2010-11-12 price VBMPX 158.66 USD 2010-11-12 price RGAGX 288.93 USD 2010-11-12 price ITOT 77.99 USD 2010-11-12 price VEA 131.21 USD 2010-11-12 price VHT 77.69 USD 2010-11-12 price GLD 78.54 USD 2010-11-19 price VBMPX 163.56 USD 2010-11-19 price RGAGX 295.84 USD 2010-11-19 price ITOT 79.74 USD 2010-11-19 price VEA 132.50 USD 2010-11-19 price VHT 78.29 USD 2010-11-19 price GLD 78.82 USD 2010-11-26 price VBMPX 163.45 USD 2010-11-26 price RGAGX 297.45 USD 2010-11-26 price ITOT 79.58 USD 2010-11-26 price VEA 133.54 USD 2010-11-26 price VHT 80.10 USD 2010-11-26 price GLD 78.12 USD 2010-12-03 price VBMPX 158.62 USD 2010-12-03 price RGAGX 293.41 USD 2010-12-03 price ITOT 79.02 USD 2010-12-03 price VEA 134.38 USD 2010-12-03 price VHT 80.17 USD 2010-12-03 price GLD 77.31 USD 2010-12-10 price VBMPX 160.16 USD 2010-12-10 price RGAGX 290.31 USD 2010-12-10 price ITOT 79.29 USD 2010-12-10 price VEA 133.60 USD 2010-12-10 price VHT 78.90 USD 2010-12-10 price GLD 77.32 USD 2010-12-17 price VBMPX 154.91 USD 2010-12-17 price RGAGX 288.84 USD 2010-12-17 price ITOT 80.78 USD 2010-12-17 price VEA 133.81 USD 2010-12-17 price VHT 79.73 USD 2010-12-17 price GLD 76.16 USD 2010-12-24 price VBMPX 154.20 USD 2010-12-24 price RGAGX 291.90 USD 2010-12-24 price ITOT 80.06 USD 2010-12-24 price VEA 134.98 USD 2010-12-24 price VHT 79.55 USD 2010-12-24 price GLD 77.30 USD 2010-12-31 price VBMPX 156.71 USD 2010-12-31 price RGAGX 286.58 USD 2010-12-31 price ITOT 82.72 USD 2010-12-31 price VEA 135.67 USD 2010-12-31 price VHT 82.36 USD 2010-12-31 price GLD 76.80 USD 2011-01-07 price VBMPX 158.66 USD 2011-01-07 price RGAGX 293.61 USD 2011-01-07 price ITOT 82.12 USD 2011-01-07 price VEA 135.26 USD 2011-01-07 price VHT 83.26 USD 2011-01-07 price GLD 76.61 USD 2011-01-14 price VBMPX 161.30 USD 2011-01-14 price RGAGX 294.97 USD 2011-01-14 price ITOT 82.75 USD 2011-01-14 price VEA 133.82 USD 2011-01-14 price VHT 82.96 USD 2011-01-14 price GLD 75.25 USD 2011-01-21 price VBMPX 162.25 USD 2011-01-21 price RGAGX 296.41 USD 2011-01-21 price ITOT 81.61 USD 2011-01-21 price VEA 134.13 USD 2011-01-21 price VHT 83.21 USD 2011-01-21 price GLD 75.75 USD 2011-01-28 price VBMPX 164.27 USD 2011-01-28 price RGAGX 293.68 USD 2011-01-28 price ITOT 81.82 USD 2011-01-28 price VEA 134.64 USD 2011-01-28 price VHT 82.98 USD 2011-01-28 price GLD 74.98 USD 2011-02-04 price VBMPX 167.21 USD 2011-02-04 price RGAGX 296.11 USD 2011-02-04 price ITOT 82.40 USD 2011-02-04 price VEA 136.11 USD 2011-02-04 price VHT 83.69 USD 2011-02-04 price GLD 75.97 USD 2011-02-11 price VBMPX 167.70 USD 2011-02-11 price RGAGX 293.86 USD 2011-02-11 price ITOT 81.91 USD 2011-02-11 price VEA 135.70 USD 2011-02-11 price VHT 83.21 USD 2011-02-11 price GLD 75.13 USD 2011-02-18 price VBMPX 170.83 USD 2011-02-18 price RGAGX 289.64 USD 2011-02-18 price ITOT 84.04 USD 2011-02-18 price VEA 136.74 USD 2011-02-18 price VHT 82.41 USD 2011-02-18 price GLD 76.10 USD 2011-02-25 price VBMPX 170.74 USD 2011-02-25 price RGAGX 291.68 USD 2011-02-25 price ITOT 84.21 USD 2011-02-25 price VEA 135.55 USD 2011-02-25 price VHT 82.73 USD 2011-02-25 price GLD 76.53 USD 2011-03-04 price VBMPX 168.88 USD 2011-03-04 price RGAGX 291.68 USD 2011-03-04 price ITOT 85.71 USD 2011-03-04 price VEA 135.98 USD 2011-03-04 price VHT 82.61 USD 2011-03-04 price GLD 78.20 USD 2011-03-11 price VBMPX 171.25 USD 2011-03-11 price RGAGX 292.98 USD 2011-03-11 price ITOT 84.71 USD 2011-03-11 price VEA 133.79 USD 2011-03-11 price VHT 83.89 USD 2011-03-11 price GLD 80.85 USD 2011-03-18 price VBMPX 169.40 USD 2011-03-18 price RGAGX 292.00 USD 2011-03-18 price ITOT 85.54 USD 2011-03-18 price VEA 133.68 USD 2011-03-18 price VHT 84.03 USD 2011-03-18 price GLD 80.91 USD 2011-03-25 price VBMPX 165.97 USD 2011-03-25 price RGAGX 294.58 USD 2011-03-25 price ITOT 87.64 USD 2011-03-25 price VEA 133.48 USD 2011-03-25 price VHT 84.31 USD 2011-03-25 price GLD 81.67 USD 2011-04-01 price VBMPX 168.19 USD 2011-04-01 price RGAGX 294.64 USD 2011-04-01 price ITOT 86.48 USD 2011-04-01 price VEA 134.14 USD 2011-04-01 price VHT 83.01 USD 2011-04-01 price GLD 81.12 USD 2011-04-08 price VBMPX 170.00 USD 2011-04-08 price RGAGX 294.50 USD 2011-04-08 price ITOT 86.14 USD 2011-04-08 price VEA 135.66 USD 2011-04-08 price VHT 83.55 USD 2011-04-08 price GLD 81.61 USD 2011-04-15 price VBMPX 166.08 USD 2011-04-15 price RGAGX 299.37 USD 2011-04-15 price ITOT 83.50 USD 2011-04-15 price VEA 134.64 USD 2011-04-15 price VHT 84.43 USD 2011-04-15 price GLD 80.43 USD 2011-04-22 price VBMPX 169.32 USD 2011-04-22 price RGAGX 305.02 USD 2011-04-22 price ITOT 82.68 USD 2011-04-22 price VEA 134.15 USD 2011-04-22 price VHT 83.85 USD 2011-04-22 price GLD 82.31 USD 2011-04-29 price VBMPX 172.68 USD 2011-04-29 price RGAGX 306.61 USD 2011-04-29 price ITOT 84.01 USD 2011-04-29 price VEA 135.89 USD 2011-04-29 price VHT 82.99 USD 2011-04-29 price GLD 85.29 USD 2011-05-06 price VBMPX 172.70 USD 2011-05-06 price RGAGX 307.46 USD 2011-05-06 price ITOT 85.61 USD 2011-05-06 price VEA 136.48 USD 2011-05-06 price VHT 84.37 USD 2011-05-06 price GLD 86.15 USD 2011-05-13 price VBMPX 179.43 USD 2011-05-13 price RGAGX 316.92 USD 2011-05-13 price ITOT 86.78 USD 2011-05-13 price VEA 137.88 USD 2011-05-13 price VHT 85.06 USD 2011-05-13 price GLD 87.66 USD 2011-05-20 price VBMPX 175.69 USD 2011-05-20 price RGAGX 316.04 USD 2011-05-20 price ITOT 88.02 USD 2011-05-20 price VEA 137.49 USD 2011-05-20 price VHT 84.86 USD 2011-05-20 price GLD 89.14 USD 2011-05-27 price VBMPX 172.76 USD 2011-05-27 price RGAGX 319.64 USD 2011-05-27 price ITOT 87.63 USD 2011-05-27 price VEA 138.21 USD 2011-05-27 price VHT 84.01 USD 2011-05-27 price GLD 87.61 USD 2011-06-03 price VBMPX 170.02 USD 2011-06-03 price RGAGX 312.33 USD 2011-06-03 price ITOT 87.13 USD 2011-06-03 price VEA 140.78 USD 2011-06-03 price VHT 86.62 USD 2011-06-03 price GLD 86.18 USD 2011-06-10 price VBMPX 169.80 USD 2011-06-10 price RGAGX 319.88 USD 2011-06-10 price ITOT 85.21 USD 2011-06-10 price VEA 140.91 USD 2011-06-10 price VHT 84.77 USD 2011-06-10 price GLD 85.88 USD 2011-06-17 price VBMPX 171.85 USD 2011-06-17 price RGAGX 325.96 USD 2011-06-17 price ITOT 82.92 USD 2011-06-17 price VEA 141.56 USD 2011-06-17 price VHT 85.20 USD 2011-06-17 price GLD 86.78 USD 2011-06-24 price VBMPX 173.83 USD 2011-06-24 price RGAGX 327.83 USD 2011-06-24 price ITOT 82.25 USD 2011-06-24 price VEA 141.47 USD 2011-06-24 price VHT 85.20 USD 2011-06-24 price GLD 85.26 USD 2011-07-01 price VBMPX 172.97 USD 2011-07-01 price RGAGX 332.10 USD 2011-07-01 price ITOT 82.93 USD 2011-07-01 price VEA 142.32 USD 2011-07-01 price VHT 85.28 USD 2011-07-01 price GLD 87.42 USD 2011-07-08 price VBMPX 171.53 USD 2011-07-08 price RGAGX 333.05 USD 2011-07-08 price ITOT 82.81 USD 2011-07-08 price VEA 144.68 USD 2011-07-08 price VHT 86.97 USD 2011-07-08 price GLD 88.48 USD 2011-07-15 price VBMPX 161.16 USD 2011-07-15 price RGAGX 337.85 USD 2011-07-15 price ITOT 82.37 USD 2011-07-15 price VEA 144.43 USD 2011-07-15 price VHT 86.48 USD 2011-07-15 price GLD 88.41 USD 2011-07-22 price VBMPX 162.92 USD 2011-07-22 price RGAGX 332.54 USD 2011-07-22 price ITOT 83.29 USD 2011-07-22 price VEA 145.00 USD 2011-07-22 price VHT 85.01 USD 2011-07-22 price GLD 85.64 USD 2011-07-29 price VBMPX 167.14 USD 2011-07-29 price RGAGX 335.56 USD 2011-07-29 price ITOT 84.83 USD 2011-07-29 price VEA 146.06 USD 2011-07-29 price VHT 84.48 USD 2011-07-29 price GLD 86.26 USD 2011-08-05 price VBMPX 165.59 USD 2011-08-05 price RGAGX 334.94 USD 2011-08-05 price ITOT 82.80 USD 2011-08-05 price VEA 149.07 USD 2011-08-05 price VHT 85.28 USD 2011-08-05 price GLD 87.12 USD 2011-08-12 price VBMPX 167.99 USD 2011-08-12 price RGAGX 338.55 USD 2011-08-12 price ITOT 82.45 USD 2011-08-12 price VEA 148.49 USD 2011-08-12 price VHT 84.24 USD 2011-08-12 price GLD 88.81 USD 2011-08-19 price VBMPX 166.52 USD 2011-08-19 price RGAGX 344.66 USD 2011-08-19 price ITOT 84.55 USD 2011-08-19 price VEA 149.16 USD 2011-08-19 price VHT 84.34 USD 2011-08-19 price GLD 88.75 USD 2011-08-26 price VBMPX 165.60 USD 2011-08-26 price RGAGX 341.36 USD 2011-08-26 price ITOT 84.79 USD 2011-08-26 price VEA 150.97 USD 2011-08-26 price VHT 80.56 USD 2011-08-26 price GLD 89.34 USD 2011-09-02 price VBMPX 165.22 USD 2011-09-02 price RGAGX 341.03 USD 2011-09-02 price ITOT 85.79 USD 2011-09-02 price VEA 152.11 USD 2011-09-02 price VHT 80.70 USD 2011-09-02 price GLD 89.06 USD 2011-09-09 price VBMPX 165.68 USD 2011-09-09 price RGAGX 337.90 USD 2011-09-09 price ITOT 83.89 USD 2011-09-09 price VEA 152.57 USD 2011-09-09 price VHT 80.87 USD 2011-09-09 price GLD 88.92 USD 2011-09-16 price VBMPX 168.92 USD 2011-09-16 price RGAGX 341.95 USD 2011-09-16 price ITOT 85.04 USD 2011-09-16 price VEA 150.01 USD 2011-09-16 price VHT 80.63 USD 2011-09-16 price GLD 87.53 USD 2011-09-23 price VBMPX 168.82 USD 2011-09-23 price RGAGX 339.78 USD 2011-09-23 price ITOT 84.83 USD 2011-09-23 price VEA 150.37 USD 2011-09-23 price VHT 80.83 USD 2011-09-23 price GLD 83.40 USD 2011-09-30 price VBMPX 174.70 USD 2011-09-30 price RGAGX 338.32 USD 2011-09-30 price ITOT 83.76 USD 2011-09-30 price VEA 150.23 USD 2011-09-30 price VHT 81.83 USD 2011-09-30 price GLD 84.66 USD 2011-10-07 price VBMPX 178.40 USD 2011-10-07 price RGAGX 333.51 USD 2011-10-07 price ITOT 84.09 USD 2011-10-07 price VEA 151.84 USD 2011-10-07 price VHT 85.30 USD 2011-10-07 price GLD 85.76 USD 2011-10-14 price VBMPX 179.70 USD 2011-10-14 price RGAGX 337.51 USD 2011-10-14 price ITOT 84.04 USD 2011-10-14 price VEA 151.67 USD 2011-10-14 price VHT 85.37 USD 2011-10-14 price GLD 84.54 USD 2011-10-21 price VBMPX 181.03 USD 2011-10-21 price RGAGX 336.61 USD 2011-10-21 price ITOT 87.34 USD 2011-10-21 price VEA 153.25 USD 2011-10-21 price VHT 86.28 USD 2011-10-21 price GLD 83.54 USD 2011-10-28 price VBMPX 179.64 USD 2011-10-28 price RGAGX 338.71 USD 2011-10-28 price ITOT 87.83 USD 2011-10-28 price VEA 153.42 USD 2011-10-28 price VHT 87.47 USD 2011-10-28 price GLD 82.05 USD 2011-11-04 price VBMPX 182.71 USD 2011-11-04 price RGAGX 336.90 USD 2011-11-04 price ITOT 87.58 USD 2011-11-04 price VEA 154.17 USD 2011-11-04 price VHT 86.36 USD 2011-11-04 price GLD 84.29 USD 2011-11-11 price VBMPX 185.51 USD 2011-11-11 price RGAGX 339.61 USD 2011-11-11 price ITOT 87.97 USD 2011-11-11 price VEA 154.43 USD 2011-11-11 price VHT 87.03 USD 2011-11-11 price GLD 81.20 USD 2011-11-18 price VBMPX 189.93 USD 2011-11-18 price RGAGX 344.31 USD 2011-11-18 price ITOT 85.56 USD 2011-11-18 price VEA 153.82 USD 2011-11-18 price VHT 87.12 USD 2011-11-18 price GLD 81.56 USD 2011-11-25 price VBMPX 192.38 USD 2011-11-25 price RGAGX 350.06 USD 2011-11-25 price ITOT 87.45 USD 2011-11-25 price VEA 156.42 USD 2011-11-25 price VHT 87.03 USD 2011-11-25 price GLD 78.72 USD 2011-12-02 price VBMPX 190.90 USD 2011-12-02 price RGAGX 359.48 USD 2011-12-02 price ITOT 87.46 USD 2011-12-02 price VEA 156.73 USD 2011-12-02 price VHT 85.81 USD 2011-12-02 price GLD 77.43 USD 2011-12-09 price VBMPX 187.93 USD 2011-12-09 price RGAGX 359.63 USD 2011-12-09 price ITOT 87.40 USD 2011-12-09 price VEA 157.00 USD 2011-12-09 price VHT 87.61 USD 2011-12-09 price GLD 75.61 USD 2011-12-16 price VBMPX 181.99 USD 2011-12-16 price RGAGX 358.47 USD 2011-12-16 price ITOT 87.39 USD 2011-12-16 price VEA 156.84 USD 2011-12-16 price VHT 87.62 USD 2011-12-16 price GLD 74.05 USD 2011-12-23 price VBMPX 186.45 USD 2011-12-23 price RGAGX 360.36 USD 2011-12-23 price ITOT 89.44 USD 2011-12-23 price VEA 156.56 USD 2011-12-23 price VHT 88.06 USD 2011-12-23 price GLD 74.43 USD 2011-12-30 price VBMPX 193.66 USD 2011-12-30 price RGAGX 360.39 USD 2011-12-30 price ITOT 91.12 USD 2011-12-30 price VEA 157.15 USD 2011-12-30 price VHT 87.52 USD 2011-12-30 price GLD 74.47 USD 2012-01-06 price VBMPX 198.29 USD 2012-01-06 price RGAGX 364.25 USD 2012-01-06 price ITOT 90.90 USD 2012-01-06 price VEA 157.73 USD 2012-01-06 price VHT 87.18 USD 2012-01-06 price GLD 74.40 USD 2012-01-13 price VBMPX 199.24 USD 2012-01-13 price RGAGX 367.90 USD 2012-01-13 price ITOT 91.40 USD 2012-01-13 price VEA 158.66 USD 2012-01-13 price VHT 89.72 USD 2012-01-13 price GLD 72.49 USD 2012-01-20 price VBMPX 199.38 USD 2012-01-20 price RGAGX 361.96 USD 2012-01-20 price ITOT 92.52 USD 2012-01-20 price VEA 160.64 USD 2012-01-20 price VHT 89.22 USD 2012-01-20 price GLD 73.05 USD 2012-01-27 price VBMPX 205.30 USD 2012-01-27 price RGAGX 364.94 USD 2012-01-27 price ITOT 93.86 USD 2012-01-27 price VEA 161.86 USD 2012-01-27 price VHT 90.00 USD 2012-01-27 price GLD 71.72 USD 2012-02-03 price VBMPX 210.28 USD 2012-02-03 price RGAGX 377.27 USD 2012-02-03 price ITOT 94.17 USD 2012-02-03 price VEA 163.73 USD 2012-02-03 price VHT 88.00 USD 2012-02-03 price GLD 72.31 USD 2012-02-10 price VBMPX 214.99 USD 2012-02-10 price RGAGX 376.79 USD 2012-02-10 price ITOT 96.05 USD 2012-02-10 price VEA 165.41 USD 2012-02-10 price VHT 89.47 USD 2012-02-10 price GLD 70.90 USD 2012-02-17 price VBMPX 213.57 USD 2012-02-17 price RGAGX 373.05 USD 2012-02-17 price ITOT 95.19 USD 2012-02-17 price VEA 167.42 USD 2012-02-17 price VHT 91.10 USD 2012-02-17 price GLD 70.29 USD 2012-02-24 price VBMPX 215.38 USD 2012-02-24 price RGAGX 374.98 USD 2012-02-24 price ITOT 95.91 USD 2012-02-24 price VEA 167.63 USD 2012-02-24 price VHT 90.35 USD 2012-02-24 price GLD 71.58 USD 2012-03-02 price VBMPX 213.97 USD 2012-03-02 price RGAGX 373.40 USD 2012-03-02 price ITOT 98.01 USD 2012-03-02 price VEA 166.70 USD 2012-03-02 price VHT 89.65 USD 2012-03-02 price GLD 74.26 USD 2012-03-09 price VBMPX 215.25 USD 2012-03-09 price RGAGX 373.79 USD 2012-03-09 price ITOT 97.45 USD 2012-03-09 price VEA 165.98 USD 2012-03-09 price VHT 88.11 USD 2012-03-09 price GLD 70.72 USD 2012-03-16 price VBMPX 223.47 USD 2012-03-16 price RGAGX 364.98 USD 2012-03-16 price ITOT 97.11 USD 2012-03-16 price VEA 168.22 USD 2012-03-16 price VHT 88.33 USD 2012-03-16 price GLD 72.03 USD 2012-03-23 price VBMPX 220.85 USD 2012-03-23 price RGAGX 369.90 USD 2012-03-23 price ITOT 97.62 USD 2012-03-23 price VEA 169.15 USD 2012-03-23 price VHT 88.83 USD 2012-03-23 price GLD 71.40 USD 2012-03-30 price VBMPX 223.53 USD 2012-03-30 price RGAGX 364.01 USD 2012-03-30 price ITOT 97.40 USD 2012-03-30 price VEA 169.23 USD 2012-03-30 price VHT 90.27 USD 2012-03-30 price GLD 70.82 USD 2012-04-06 price VBMPX 220.06 USD 2012-04-06 price RGAGX 364.23 USD 2012-04-06 price ITOT 96.13 USD 2012-04-06 price VEA 170.32 USD 2012-04-06 price VHT 90.40 USD 2012-04-06 price GLD 69.76 USD 2012-04-13 price VBMPX 218.98 USD 2012-04-13 price RGAGX 368.49 USD 2012-04-13 price ITOT 99.29 USD 2012-04-13 price VEA 171.54 USD 2012-04-13 price VHT 90.72 USD 2012-04-13 price GLD 69.81 USD 2012-04-20 price VBMPX 216.30 USD 2012-04-20 price RGAGX 371.45 USD 2012-04-20 price ITOT 99.38 USD 2012-04-20 price VEA 172.78 USD 2012-04-20 price VHT 90.28 USD 2012-04-20 price GLD 68.50 USD 2012-04-27 price VBMPX 217.94 USD 2012-04-27 price RGAGX 370.84 USD 2012-04-27 price ITOT 99.76 USD 2012-04-27 price VEA 175.61 USD 2012-04-27 price VHT 91.63 USD 2012-04-27 price GLD 66.69 USD 2012-05-04 price VBMPX 211.40 USD 2012-05-04 price RGAGX 369.99 USD 2012-05-04 price ITOT 98.43 USD 2012-05-04 price VEA 175.38 USD 2012-05-04 price VHT 90.46 USD 2012-05-04 price GLD 68.89 USD 2012-05-11 price VBMPX 210.95 USD 2012-05-11 price RGAGX 369.62 USD 2012-05-11 price ITOT 98.59 USD 2012-05-11 price VEA 175.61 USD 2012-05-11 price VHT 90.37 USD 2012-05-11 price GLD 71.83 USD 2012-05-18 price VBMPX 210.50 USD 2012-05-18 price RGAGX 370.37 USD 2012-05-18 price ITOT 97.35 USD 2012-05-18 price VEA 176.93 USD 2012-05-18 price VHT 90.63 USD 2012-05-18 price GLD 72.42 USD 2012-05-25 price VBMPX 208.87 USD 2012-05-25 price RGAGX 374.53 USD 2012-05-25 price ITOT 98.55 USD 2012-05-25 price VEA 177.12 USD 2012-05-25 price VHT 91.52 USD 2012-05-25 price GLD 71.48 USD 2012-06-01 price VBMPX 212.15 USD 2012-06-01 price RGAGX 377.54 USD 2012-06-01 price ITOT 98.13 USD 2012-06-01 price VEA 175.85 USD 2012-06-01 price VHT 90.23 USD 2012-06-01 price GLD 70.82 USD 2012-06-08 price VBMPX 213.77 USD 2012-06-08 price RGAGX 377.55 USD 2012-06-08 price ITOT 97.64 USD 2012-06-08 price VEA 177.48 USD 2012-06-08 price VHT 90.88 USD 2012-06-08 price GLD 69.99 USD 2012-06-15 price VBMPX 208.09 USD 2012-06-15 price RGAGX 378.62 USD 2012-06-15 price ITOT 97.11 USD 2012-06-15 price VEA 179.32 USD 2012-06-15 price VHT 91.24 USD 2012-06-15 price GLD 70.55 USD 2012-06-22 price VBMPX 212.56 USD 2012-06-22 price RGAGX 373.86 USD 2012-06-22 price ITOT 99.75 USD 2012-06-22 price VEA 181.06 USD 2012-06-22 price VHT 89.85 USD 2012-06-22 price GLD 70.84 USD 2012-06-29 price VBMPX 211.46 USD 2012-06-29 price RGAGX 369.57 USD 2012-06-29 price ITOT 99.04 USD 2012-06-29 price VEA 180.70 USD 2012-06-29 price VHT 88.87 USD 2012-06-29 price GLD 71.52 USD 2012-07-06 price VBMPX 213.80 USD 2012-07-06 price RGAGX 365.92 USD 2012-07-06 price ITOT 99.05 USD 2012-07-06 price VEA 180.31 USD 2012-07-06 price VHT 89.64 USD 2012-07-06 price GLD 71.12 USD 2012-07-13 price VBMPX 211.69 USD 2012-07-13 price RGAGX 374.63 USD 2012-07-13 price ITOT 99.43 USD 2012-07-13 price VEA 182.17 USD 2012-07-13 price VHT 89.58 USD 2012-07-13 price GLD 69.80 USD 2012-07-20 price VBMPX 222.40 USD 2012-07-20 price RGAGX 382.93 USD 2012-07-20 price ITOT 98.89 USD 2012-07-20 price VEA 180.98 USD 2012-07-20 price VHT 90.50 USD 2012-07-20 price GLD 68.87 USD 2012-07-27 price VBMPX 215.18 USD 2012-07-27 price RGAGX 379.86 USD 2012-07-27 price ITOT 98.60 USD 2012-07-27 price VEA 182.07 USD 2012-07-27 price VHT 89.65 USD 2012-07-27 price GLD 69.53 USD 2012-08-03 price VBMPX 213.12 USD 2012-08-03 price RGAGX 387.62 USD 2012-08-03 price ITOT 97.24 USD 2012-08-03 price VEA 182.30 USD 2012-08-03 price VHT 89.52 USD 2012-08-03 price GLD 69.50 USD 2012-08-10 price VBMPX 215.68 USD 2012-08-10 price RGAGX 383.07 USD 2012-08-10 price ITOT 97.45 USD 2012-08-10 price VEA 181.61 USD 2012-08-10 price VHT 89.68 USD 2012-08-10 price GLD 67.79 USD 2012-08-17 price VBMPX 223.57 USD 2012-08-17 price RGAGX 391.59 USD 2012-08-17 price ITOT 97.65 USD 2012-08-17 price VEA 180.87 USD 2012-08-17 price VHT 90.32 USD 2012-08-17 price GLD 66.89 USD 2012-08-24 price VBMPX 228.80 USD 2012-08-24 price RGAGX 393.48 USD 2012-08-24 price ITOT 96.53 USD 2012-08-24 price VEA 179.89 USD 2012-08-24 price VHT 89.53 USD 2012-08-24 price GLD 66.15 USD 2012-08-31 price VBMPX 226.38 USD 2012-08-31 price RGAGX 394.75 USD 2012-08-31 price ITOT 97.60 USD 2012-08-31 price VEA 180.80 USD 2012-08-31 price VHT 90.02 USD 2012-08-31 price GLD 65.58 USD 2012-09-07 price VBMPX 225.17 USD 2012-09-07 price RGAGX 399.72 USD 2012-09-07 price ITOT 97.78 USD 2012-09-07 price VEA 181.89 USD 2012-09-07 price VHT 90.28 USD 2012-09-07 price GLD 68.18 USD 2012-09-14 price VBMPX 229.08 USD 2012-09-14 price RGAGX 404.53 USD 2012-09-14 price ITOT 98.41 USD 2012-09-14 price VEA 181.77 USD 2012-09-14 price VHT 90.90 USD 2012-09-14 price GLD 65.80 USD 2012-09-21 price VBMPX 225.47 USD 2012-09-21 price RGAGX 401.40 USD 2012-09-21 price ITOT 99.22 USD 2012-09-21 price VEA 183.64 USD 2012-09-21 price VHT 90.40 USD 2012-09-21 price GLD 65.36 USD 2012-09-28 price VBMPX 228.60 USD 2012-09-28 price RGAGX 402.33 USD 2012-09-28 price ITOT 99.01 USD 2012-09-28 price VEA 186.02 USD 2012-09-28 price VHT 91.24 USD 2012-09-28 price GLD 64.77 USD 2012-10-05 price VBMPX 233.08 USD 2012-10-05 price RGAGX 399.49 USD 2012-10-05 price ITOT 98.14 USD 2012-10-05 price VEA 186.02 USD 2012-10-05 price VHT 92.16 USD 2012-10-05 price GLD 64.40 USD 2012-10-12 price VBMPX 233.74 USD 2012-10-12 price RGAGX 411.05 USD 2012-10-12 price ITOT 97.40 USD 2012-10-12 price VEA 186.24 USD 2012-10-12 price VHT 91.84 USD 2012-10-12 price GLD 63.65 USD 2012-10-19 price VBMPX 237.76 USD 2012-10-19 price RGAGX 411.15 USD 2012-10-19 price ITOT 98.03 USD 2012-10-19 price VEA 184.94 USD 2012-10-19 price VHT 89.81 USD 2012-10-19 price GLD 66.84 USD 2012-10-26 price VBMPX 237.87 USD 2012-10-26 price RGAGX 410.17 USD 2012-10-26 price ITOT 97.55 USD 2012-10-26 price VEA 186.58 USD 2012-10-26 price VHT 90.84 USD 2012-10-26 price GLD 67.74 USD 2012-11-02 price VBMPX 243.03 USD 2012-11-02 price RGAGX 410.41 USD 2012-11-02 price ITOT 100.05 USD 2012-11-02 price VEA 187.05 USD 2012-11-02 price VHT 90.72 USD 2012-11-02 price GLD 67.04 USD 2012-11-09 price VBMPX 248.71 USD 2012-11-09 price RGAGX 415.71 USD 2012-11-09 price ITOT 97.75 USD 2012-11-09 price VEA 187.55 USD 2012-11-09 price VHT 92.32 USD 2012-11-09 price GLD 69.10 USD 2012-11-16 price VBMPX 252.35 USD 2012-11-16 price RGAGX 409.72 USD 2012-11-16 price ITOT 98.30 USD 2012-11-16 price VEA 187.94 USD 2012-11-16 price VHT 92.53 USD 2012-11-16 price GLD 68.78 USD 2012-11-23 price VBMPX 251.40 USD 2012-11-23 price RGAGX 411.15 USD 2012-11-23 price ITOT 98.78 USD 2012-11-23 price VEA 188.41 USD 2012-11-23 price VHT 93.45 USD 2012-11-23 price GLD 71.25 USD 2012-11-30 price VBMPX 247.50 USD 2012-11-30 price RGAGX 412.92 USD 2012-11-30 price ITOT 98.84 USD 2012-11-30 price VEA 188.86 USD 2012-11-30 price VHT 92.96 USD 2012-11-30 price GLD 70.32 USD 2012-12-07 price VBMPX 247.67 USD 2012-12-07 price RGAGX 420.02 USD 2012-12-07 price ITOT 99.99 USD 2012-12-07 price VEA 188.54 USD 2012-12-07 price VHT 92.59 USD 2012-12-07 price GLD 70.65 USD 2012-12-14 price VBMPX 251.47 USD 2012-12-14 price RGAGX 427.06 USD 2012-12-14 price ITOT 98.58 USD 2012-12-14 price VEA 188.40 USD 2012-12-14 price VHT 95.03 USD 2012-12-14 price GLD 72.49 USD 2012-12-21 price VBMPX 249.08 USD 2012-12-21 price RGAGX 422.58 USD 2012-12-21 price ITOT 97.06 USD 2012-12-21 price VEA 188.52 USD 2012-12-21 price VHT 95.72 USD 2012-12-21 price GLD 72.98 USD 2012-12-28 price VBMPX 248.47 USD 2012-12-28 price RGAGX 419.20 USD 2012-12-28 price ITOT 97.19 USD 2012-12-28 price VEA 188.13 USD 2012-12-28 price VHT 95.94 USD 2012-12-28 price GLD 71.80 USD 2013-01-04 price VBMPX 247.67 USD 2013-01-04 price RGAGX 422.71 USD 2013-01-04 price ITOT 96.93 USD 2013-01-04 price VEA 189.65 USD 2013-01-04 price VHT 95.97 USD 2013-01-04 price GLD 72.74 USD 2013-01-11 price VBMPX 240.44 USD 2013-01-11 price RGAGX 429.27 USD 2013-01-11 price ITOT 98.91 USD 2013-01-11 price VEA 188.63 USD 2013-01-11 price VHT 94.55 USD 2013-01-11 price GLD 74.80 USD 2013-01-18 price VBMPX 244.96 USD 2013-01-18 price RGAGX 421.70 USD 2013-01-18 price ITOT 100.83 USD 2013-01-18 price VEA 189.39 USD 2013-01-18 price VHT 95.48 USD 2013-01-18 price GLD 75.92 USD 2013-01-25 price VBMPX 243.83 USD 2013-01-25 price RGAGX 421.05 USD 2013-01-25 price ITOT 103.40 USD 2013-01-25 price VEA 187.70 USD 2013-01-25 price VHT 95.53 USD 2013-01-25 price GLD 76.31 USD 2013-02-01 price VBMPX 250.44 USD 2013-02-01 price RGAGX 414.03 USD 2013-02-01 price ITOT 102.62 USD 2013-02-01 price VEA 188.41 USD 2013-02-01 price VHT 97.53 USD 2013-02-01 price GLD 75.50 USD 2013-02-08 price VBMPX 249.39 USD 2013-02-08 price RGAGX 413.48 USD 2013-02-08 price ITOT 98.69 USD 2013-02-08 price VEA 190.20 USD 2013-02-08 price VHT 97.60 USD 2013-02-08 price GLD 77.62 USD 2013-02-15 price VBMPX 241.32 USD 2013-02-15 price RGAGX 412.96 USD 2013-02-15 price ITOT 98.75 USD 2013-02-15 price VEA 191.27 USD 2013-02-15 price VHT 96.27 USD 2013-02-15 price GLD 76.06 USD 2013-02-22 price VBMPX 237.78 USD 2013-02-22 price RGAGX 418.62 USD 2013-02-22 price ITOT 99.30 USD 2013-02-22 price VEA 191.76 USD 2013-02-22 price VHT 97.51 USD 2013-02-22 price GLD 75.07 USD 2013-03-01 price VBMPX 238.35 USD 2013-03-01 price RGAGX 421.45 USD 2013-03-01 price ITOT 99.05 USD 2013-03-01 price VEA 189.55 USD 2013-03-01 price VHT 98.29 USD 2013-03-01 price GLD 76.61 USD 2013-03-08 price VBMPX 236.58 USD 2013-03-08 price RGAGX 424.97 USD 2013-03-08 price ITOT 99.81 USD 2013-03-08 price VEA 188.86 USD 2013-03-08 price VHT 98.13 USD 2013-03-08 price GLD 74.65 USD 2013-03-15 price VBMPX 233.37 USD 2013-03-15 price RGAGX 427.40 USD 2013-03-15 price ITOT 100.25 USD 2013-03-15 price VEA 191.07 USD 2013-03-15 price VHT 96.27 USD 2013-03-15 price GLD 76.26 USD 2013-03-22 price VBMPX 227.04 USD 2013-03-22 price RGAGX 432.32 USD 2013-03-22 price ITOT 100.90 USD 2013-03-22 price VEA 190.11 USD 2013-03-22 price VHT 98.10 USD 2013-03-22 price GLD 74.87 USD 2013-03-29 price VBMPX 218.84 USD 2013-03-29 price RGAGX 440.62 USD 2013-03-29 price ITOT 100.89 USD 2013-03-29 price VEA 190.88 USD 2013-03-29 price VHT 100.64 USD 2013-03-29 price GLD 75.05 USD 2013-04-05 price VBMPX 221.29 USD 2013-04-05 price RGAGX 439.16 USD 2013-04-05 price ITOT 103.62 USD 2013-04-05 price VEA 193.09 USD 2013-04-05 price VHT 100.09 USD 2013-04-05 price GLD 74.04 USD 2013-04-12 price VBMPX 211.62 USD 2013-04-12 price RGAGX 442.59 USD 2013-04-12 price ITOT 103.59 USD 2013-04-12 price VEA 191.26 USD 2013-04-12 price VHT 98.81 USD 2013-04-12 price GLD 74.93 USD 2013-04-19 price VBMPX 212.29 USD 2013-04-19 price RGAGX 440.93 USD 2013-04-19 price ITOT 103.61 USD 2013-04-19 price VEA 191.67 USD 2013-04-19 price VHT 97.34 USD 2013-04-19 price GLD 74.77 USD 2013-04-26 price VBMPX 218.25 USD 2013-04-26 price RGAGX 449.53 USD 2013-04-26 price ITOT 105.77 USD 2013-04-26 price VEA 194.37 USD 2013-04-26 price VHT 98.98 USD 2013-04-26 price GLD 76.58 USD 2013-05-03 price VBMPX 219.92 USD 2013-05-03 price RGAGX 448.67 USD 2013-05-03 price ITOT 106.85 USD 2013-05-03 price VEA 194.04 USD 2013-05-03 price VHT 99.61 USD 2013-05-03 price GLD 77.47 USD 2013-05-10 price VBMPX 217.68 USD 2013-05-10 price RGAGX 450.40 USD 2013-05-10 price ITOT 108.29 USD 2013-05-10 price VEA 195.02 USD 2013-05-10 price VHT 97.41 USD 2013-05-10 price GLD 76.58 USD 2013-05-17 price VBMPX 216.92 USD 2013-05-17 price RGAGX 456.42 USD 2013-05-17 price ITOT 106.84 USD 2013-05-17 price VEA 194.46 USD 2013-05-17 price VHT 98.40 USD 2013-05-17 price GLD 74.36 USD 2013-05-24 price VBMPX 222.23 USD 2013-05-24 price RGAGX 456.10 USD 2013-05-24 price ITOT 108.49 USD 2013-05-24 price VEA 195.65 USD 2013-05-24 price VHT 97.63 USD 2013-05-24 price GLD 75.20 USD 2013-05-31 price VBMPX 226.25 USD 2013-05-31 price RGAGX 452.08 USD 2013-05-31 price ITOT 107.90 USD 2013-05-31 price VEA 195.86 USD 2013-05-31 price VHT 98.49 USD 2013-05-31 price GLD 76.05 USD 2013-06-07 price VBMPX 231.76 USD 2013-06-07 price RGAGX 449.89 USD 2013-06-07 price ITOT 107.82 USD 2013-06-07 price VEA 198.65 USD 2013-06-07 price VHT 98.26 USD 2013-06-07 price GLD 78.38 USD 2013-06-14 price VBMPX 229.89 USD 2013-06-14 price RGAGX 446.68 USD 2013-06-14 price ITOT 108.08 USD 2013-06-14 price VEA 200.60 USD 2013-06-14 price VHT 99.45 USD 2013-06-14 price GLD 77.03 USD 2013-06-21 price VBMPX 227.36 USD 2013-06-21 price RGAGX 444.88 USD 2013-06-21 price ITOT 108.24 USD 2013-06-21 price VEA 199.96 USD 2013-06-21 price VHT 98.81 USD 2013-06-21 price GLD 76.50 USD 2013-06-28 price VBMPX 230.37 USD 2013-06-28 price RGAGX 448.37 USD 2013-06-28 price ITOT 107.84 USD 2013-06-28 price VEA 202.37 USD 2013-06-28 price VHT 96.40 USD 2013-06-28 price GLD 74.51 USD 2013-07-05 price VBMPX 232.12 USD 2013-07-05 price RGAGX 455.00 USD 2013-07-05 price ITOT 112.18 USD 2013-07-05 price VEA 202.52 USD 2013-07-05 price VHT 93.31 USD 2013-07-05 price GLD 74.49 USD 2013-07-12 price VBMPX 232.33 USD 2013-07-12 price RGAGX 456.29 USD 2013-07-12 price ITOT 112.20 USD 2013-07-12 price VEA 200.63 USD 2013-07-12 price VHT 92.28 USD 2013-07-12 price GLD 76.31 USD 2013-07-19 price VBMPX 231.57 USD 2013-07-19 price RGAGX 464.17 USD 2013-07-19 price ITOT 109.91 USD 2013-07-19 price VEA 200.82 USD 2013-07-19 price VHT 93.10 USD 2013-07-19 price GLD 77.93 USD 2013-07-26 price VBMPX 237.59 USD 2013-07-26 price RGAGX 465.94 USD 2013-07-26 price ITOT 111.10 USD 2013-07-26 price VEA 199.93 USD 2013-07-26 price VHT 92.72 USD 2013-07-26 price GLD 76.80 USD 2013-08-02 price VBMPX 235.56 USD 2013-08-02 price RGAGX 456.96 USD 2013-08-02 price ITOT 110.71 USD 2013-08-02 price VEA 199.66 USD 2013-08-02 price VHT 91.53 USD 2013-08-02 price GLD 76.03 USD 2013-08-09 price VBMPX 235.09 USD 2013-08-09 price RGAGX 454.61 USD 2013-08-09 price ITOT 109.65 USD 2013-08-09 price VEA 200.79 USD 2013-08-09 price VHT 89.84 USD 2013-08-09 price GLD 75.23 USD 2013-08-16 price VBMPX 236.31 USD 2013-08-16 price RGAGX 458.71 USD 2013-08-16 price ITOT 111.85 USD 2013-08-16 price VEA 202.06 USD 2013-08-16 price VHT 89.50 USD 2013-08-16 price GLD 76.01 USD 2013-08-23 price VBMPX 240.45 USD 2013-08-23 price RGAGX 461.51 USD 2013-08-23 price ITOT 112.62 USD 2013-08-23 price VEA 202.34 USD 2013-08-23 price VHT 88.89 USD 2013-08-23 price GLD 77.93 USD 2013-08-30 price VBMPX 241.18 USD 2013-08-30 price RGAGX 460.55 USD 2013-08-30 price ITOT 111.73 USD 2013-08-30 price VEA 203.46 USD 2013-08-30 price VHT 89.44 USD 2013-08-30 price GLD 78.79 USD 2013-09-06 price VBMPX 239.06 USD 2013-09-06 price RGAGX 454.57 USD 2013-09-06 price ITOT 112.42 USD 2013-09-06 price VEA 203.86 USD 2013-09-06 price VHT 90.22 USD 2013-09-06 price GLD 80.81 USD 2013-09-13 price VBMPX 237.14 USD 2013-09-13 price RGAGX 458.03 USD 2013-09-13 price ITOT 113.15 USD 2013-09-13 price VEA 205.01 USD 2013-09-13 price VHT 89.50 USD 2013-09-13 price GLD 81.11 USD 2013-09-20 price VBMPX 236.65 USD 2013-09-20 price RGAGX 460.41 USD 2013-09-20 price ITOT 115.45 USD 2013-09-20 price VEA 201.70 USD 2013-09-20 price VHT 88.82 USD 2013-09-20 price GLD 81.83 USD 2013-09-27 price VBMPX 240.20 USD 2013-09-27 price RGAGX 458.22 USD 2013-09-27 price ITOT 118.21 USD 2013-09-27 price VEA 201.48 USD 2013-09-27 price VHT 90.85 USD 2013-09-27 price GLD 82.82 USD 2013-10-04 price VBMPX 236.04 USD 2013-10-04 price RGAGX 459.15 USD 2013-10-04 price ITOT 120.40 USD 2013-10-04 price VEA 202.80 USD 2013-10-04 price VHT 91.09 USD 2013-10-04 price GLD 81.90 USD 2013-10-11 price VBMPX 235.23 USD 2013-10-11 price RGAGX 461.29 USD 2013-10-11 price ITOT 118.74 USD 2013-10-11 price VEA 202.18 USD 2013-10-11 price VHT 91.97 USD 2013-10-11 price GLD 78.81 USD 2013-10-18 price VBMPX 238.37 USD 2013-10-18 price RGAGX 466.62 USD 2013-10-18 price ITOT 119.21 USD 2013-10-18 price VEA 202.20 USD 2013-10-18 price VHT 93.80 USD 2013-10-18 price GLD 80.46 USD 2013-10-25 price VBMPX 234.43 USD 2013-10-25 price RGAGX 475.00 USD 2013-10-25 price ITOT 116.21 USD 2013-10-25 price VEA 205.11 USD 2013-10-25 price VHT 92.46 USD 2013-10-25 price GLD 81.78 USD 2013-11-01 price VBMPX 239.01 USD 2013-11-01 price RGAGX 473.46 USD 2013-11-01 price ITOT 115.07 USD 2013-11-01 price VEA 204.17 USD 2013-11-01 price VHT 92.34 USD 2013-11-01 price GLD 82.05 USD 2013-11-08 price VBMPX 237.94 USD 2013-11-08 price RGAGX 475.75 USD 2013-11-08 price ITOT 115.01 USD 2013-11-08 price VEA 203.65 USD 2013-11-08 price VHT 93.03 USD 2013-11-08 price GLD 82.07 USD 2013-11-15 price VBMPX 239.10 USD 2013-11-15 price RGAGX 479.44 USD 2013-11-15 price ITOT 115.41 USD 2013-11-15 price VEA 207.23 USD 2013-11-15 price VHT 92.59 USD 2013-11-15 price GLD 80.88 USD 2013-11-22 price VBMPX 239.76 USD 2013-11-22 price RGAGX 473.47 USD 2013-11-22 price ITOT 113.09 USD 2013-11-22 price VEA 207.52 USD 2013-11-22 price VHT 94.25 USD 2013-11-22 price GLD 81.39 USD 2013-11-29 price VBMPX 242.15 USD 2013-11-29 price RGAGX 473.59 USD 2013-11-29 price ITOT 112.81 USD 2013-11-29 price VEA 209.35 USD 2013-11-29 price VHT 94.01 USD 2013-11-29 price GLD 82.04 USD 2013-12-06 price VBMPX 245.86 USD 2013-12-06 price RGAGX 479.66 USD 2013-12-06 price ITOT 113.90 USD 2013-12-06 price VEA 210.71 USD 2013-12-06 price VHT 93.42 USD 2013-12-06 price GLD 82.28 USD 2013-12-13 price VBMPX 243.63 USD 2013-12-13 price RGAGX 483.75 USD 2013-12-13 price ITOT 112.90 USD 2013-12-13 price VEA 211.35 USD 2013-12-13 price VHT 94.54 USD 2013-12-13 price GLD 83.08 USD 2013-12-20 price VBMPX 241.80 USD 2013-12-20 price RGAGX 484.17 USD 2013-12-20 price ITOT 112.65 USD 2013-12-20 price VEA 212.67 USD 2013-12-20 price VHT 93.05 USD 2013-12-20 price GLD 80.92 USD 2013-12-27 price VBMPX 243.89 USD 2013-12-27 price RGAGX 488.92 USD 2013-12-27 price ITOT 111.82 USD 2013-12-27 price VEA 214.01 USD 2013-12-27 price VHT 92.71 USD 2013-12-27 price GLD 84.25 USD 2014-01-03 price VBMPX 244.72 USD 2014-01-03 price RGAGX 493.83 USD 2014-01-03 price ITOT 112.82 USD 2014-01-03 price VEA 214.14 USD 2014-01-03 price VHT 94.11 USD 2014-01-03 price GLD 84.95 USD 2014-01-10 price VBMPX 245.18 USD 2014-01-10 price RGAGX 493.41 USD 2014-01-10 price ITOT 114.79 USD 2014-01-10 price VEA 217.13 USD 2014-01-10 price VHT 93.80 USD 2014-01-10 price GLD 85.87 USD 2014-01-17 price VBMPX 249.52 USD 2014-01-17 price RGAGX 489.65 USD 2014-01-17 price ITOT 116.02 USD 2014-01-17 price VEA 218.22 USD 2014-01-17 price VHT 93.58 USD 2014-01-17 price GLD 87.39 USD 2014-01-24 price VBMPX 248.68 USD 2014-01-24 price RGAGX 490.32 USD 2014-01-24 price ITOT 116.53 USD 2014-01-24 price VEA 221.94 USD 2014-01-24 price VHT 94.65 USD 2014-01-24 price GLD 87.35 USD 2014-01-31 price VBMPX 243.33 USD 2014-01-31 price RGAGX 495.47 USD 2014-01-31 price ITOT 117.55 USD 2014-01-31 price VEA 220.42 USD 2014-01-31 price VHT 93.51 USD 2014-01-31 price GLD 89.52 USD 2014-02-07 price VBMPX 245.72 USD 2014-02-07 price RGAGX 493.71 USD 2014-02-07 price ITOT 118.23 USD 2014-02-07 price VEA 220.28 USD 2014-02-07 price VHT 95.44 USD 2014-02-07 price GLD 90.42 USD 2014-02-14 price VBMPX 245.77 USD 2014-02-14 price RGAGX 487.42 USD 2014-02-14 price ITOT 119.63 USD 2014-02-14 price VEA 218.57 USD 2014-02-14 price VHT 95.80 USD 2014-02-14 price GLD 92.70 USD 2014-02-21 price VBMPX 245.00 USD 2014-02-21 price RGAGX 482.51 USD 2014-02-21 price ITOT 118.90 USD 2014-02-21 price VEA 219.25 USD 2014-02-21 price VHT 96.71 USD 2014-02-21 price GLD 93.58 USD 2014-02-28 price VBMPX 252.60 USD 2014-02-28 price RGAGX 483.46 USD 2014-02-28 price ITOT 117.35 USD 2014-02-28 price VEA 219.98 USD 2014-02-28 price VHT 96.18 USD 2014-02-28 price GLD 89.70 USD 2014-03-07 price VBMPX 248.79 USD 2014-03-07 price RGAGX 489.62 USD 2014-03-07 price ITOT 116.48 USD 2014-03-07 price VEA 221.69 USD 2014-03-07 price VHT 96.00 USD 2014-03-07 price GLD 87.59 USD 2014-03-14 price VBMPX 249.06 USD 2014-03-14 price RGAGX 488.60 USD 2014-03-14 price ITOT 117.37 USD 2014-03-14 price VEA 225.65 USD 2014-03-14 price VHT 97.64 USD 2014-03-14 price GLD 87.34 USD 2014-03-21 price VBMPX 250.87 USD 2014-03-21 price RGAGX 491.19 USD 2014-03-21 price ITOT 118.84 USD 2014-03-21 price VEA 224.73 USD 2014-03-21 price VHT 100.71 USD 2014-03-21 price GLD 90.97 USD 2014-03-28 price VBMPX 251.29 USD 2014-03-28 price RGAGX 489.44 USD 2014-03-28 price ITOT 116.78 USD 2014-03-28 price VEA 226.90 USD 2014-03-28 price VHT 100.31 USD 2014-03-28 price GLD 93.57 USD 2014-04-04 price VBMPX 259.78 USD 2014-04-04 price RGAGX 492.19 USD 2014-04-04 price ITOT 117.98 USD 2014-04-04 price VEA 229.29 USD 2014-04-04 price VHT 99.28 USD 2014-04-04 price GLD 93.98 USD 2014-04-11 price VBMPX 263.33 USD 2014-04-11 price RGAGX 484.79 USD 2014-04-11 price ITOT 116.49 USD 2014-04-11 price VEA 230.25 USD 2014-04-11 price VHT 102.25 USD 2014-04-11 price GLD 97.23 USD 2014-04-18 price VBMPX 261.60 USD 2014-04-18 price RGAGX 480.27 USD 2014-04-18 price ITOT 117.28 USD 2014-04-18 price VEA 230.86 USD 2014-04-18 price VHT 101.52 USD 2014-04-18 price GLD 98.37 USD 2014-04-25 price VBMPX 261.54 USD 2014-04-25 price RGAGX 480.28 USD 2014-04-25 price ITOT 121.38 USD 2014-04-25 price VEA 230.40 USD 2014-04-25 price VHT 101.53 USD 2014-04-25 price GLD 102.82 USD 2014-05-02 price VBMPX 259.48 USD 2014-05-02 price RGAGX 490.27 USD 2014-05-02 price ITOT 120.44 USD 2014-05-02 price VEA 229.35 USD 2014-05-02 price VHT 102.26 USD 2014-05-02 price GLD 103.23 USD 2014-05-09 price VBMPX 255.61 USD 2014-05-09 price RGAGX 486.56 USD 2014-05-09 price ITOT 117.96 USD 2014-05-09 price VEA 230.46 USD 2014-05-09 price VHT 102.62 USD 2014-05-09 price GLD 102.16 USD 2014-05-16 price VBMPX 259.09 USD 2014-05-16 price RGAGX 479.24 USD 2014-05-16 price ITOT 116.76 USD 2014-05-16 price VEA 230.07 USD 2014-05-16 price VHT 101.99 USD 2014-05-16 price GLD 102.93 USD 2014-05-23 price VBMPX 262.06 USD 2014-05-23 price RGAGX 475.06 USD 2014-05-23 price ITOT 115.60 USD 2014-05-23 price VEA 224.80 USD 2014-05-23 price VHT 104.61 USD 2014-05-23 price GLD 105.11 USD 2014-05-30 price VBMPX 261.93 USD 2014-05-30 price RGAGX 467.27 USD 2014-05-30 price ITOT 113.33 USD 2014-05-30 price VEA 223.11 USD 2014-05-30 price VHT 104.18 USD 2014-05-30 price GLD 104.04 USD 2014-06-06 price VBMPX 261.89 USD 2014-06-06 price RGAGX 467.50 USD 2014-06-06 price ITOT 112.00 USD 2014-06-06 price VEA 222.68 USD 2014-06-06 price VHT 104.12 USD 2014-06-06 price GLD 103.34 USD 2014-06-13 price VBMPX 257.59 USD 2014-06-13 price RGAGX 463.42 USD 2014-06-13 price ITOT 111.16 USD 2014-06-13 price VEA 224.12 USD 2014-06-13 price VHT 105.12 USD 2014-06-13 price GLD 101.66 USD 2014-06-20 price VBMPX 253.04 USD 2014-06-20 price RGAGX 466.36 USD 2014-06-20 price ITOT 111.59 USD 2014-06-20 price VEA 226.91 USD 2014-06-20 price VHT 105.85 USD 2014-06-20 price GLD 103.02 USD 2014-06-27 price VBMPX 253.20 USD 2014-06-27 price RGAGX 458.92 USD 2014-06-27 price ITOT 113.70 USD 2014-06-27 price VEA 227.30 USD 2014-06-27 price VHT 107.98 USD 2014-06-27 price GLD 105.15 USD 2014-07-04 price VBMPX 250.85 USD 2014-07-04 price RGAGX 458.47 USD 2014-07-04 price ITOT 114.28 USD 2014-07-04 price VEA 229.14 USD 2014-07-04 price VHT 108.60 USD 2014-07-04 price GLD 106.62 USD 2014-07-11 price VBMPX 249.53 USD 2014-07-11 price RGAGX 456.35 USD 2014-07-11 price ITOT 115.71 USD 2014-07-11 price VEA 231.22 USD 2014-07-11 price VHT 108.92 USD 2014-07-11 price GLD 104.01 USD 2014-07-18 price VBMPX 246.05 USD 2014-07-18 price RGAGX 448.81 USD 2014-07-18 price ITOT 117.22 USD 2014-07-18 price VEA 233.35 USD 2014-07-18 price VHT 108.98 USD 2014-07-18 price GLD 106.34 USD 2014-07-25 price VBMPX 240.39 USD 2014-07-25 price RGAGX 450.07 USD 2014-07-25 price ITOT 116.73 USD 2014-07-25 price VEA 230.16 USD 2014-07-25 price VHT 109.10 USD 2014-07-25 price GLD 107.97 USD 2014-08-01 price VBMPX 247.75 USD 2014-08-01 price RGAGX 458.71 USD 2014-08-01 price ITOT 116.40 USD 2014-08-01 price VEA 230.59 USD 2014-08-01 price VHT 107.96 USD 2014-08-01 price GLD 109.36 USD 2014-08-08 price VBMPX 257.11 USD 2014-08-08 price RGAGX 451.49 USD 2014-08-08 price ITOT 113.63 USD 2014-08-08 price VEA 231.59 USD 2014-08-08 price VHT 111.63 USD 2014-08-08 price GLD 110.12 USD 2014-08-15 price VBMPX 261.77 USD 2014-08-15 price RGAGX 456.39 USD 2014-08-15 price ITOT 113.51 USD 2014-08-15 price VEA 232.81 USD 2014-08-15 price VHT 112.55 USD 2014-08-15 price GLD 110.02 USD 2014-08-22 price VBMPX 265.74 USD 2014-08-22 price RGAGX 463.04 USD 2014-08-22 price ITOT 114.37 USD 2014-08-22 price VEA 230.61 USD 2014-08-22 price VHT 113.38 USD 2014-08-22 price GLD 107.96 USD 2014-08-29 price VBMPX 258.44 USD 2014-08-29 price RGAGX 462.60 USD 2014-08-29 price ITOT 116.59 USD 2014-08-29 price VEA 231.54 USD 2014-08-29 price VHT 114.93 USD 2014-08-29 price GLD 106.15 USD 2014-09-05 price VBMPX 253.03 USD 2014-09-05 price RGAGX 464.45 USD 2014-09-05 price ITOT 118.46 USD 2014-09-05 price VEA 230.71 USD 2014-09-05 price VHT 116.37 USD 2014-09-05 price GLD 109.22 USD 2014-09-12 price VBMPX 254.43 USD 2014-09-12 price RGAGX 475.08 USD 2014-09-12 price ITOT 119.61 USD 2014-09-12 price VEA 231.02 USD 2014-09-12 price VHT 115.98 USD 2014-09-12 price GLD 111.82 USD 2014-09-19 price VBMPX 250.85 USD 2014-09-19 price RGAGX 476.71 USD 2014-09-19 price ITOT 119.88 USD 2014-09-19 price VEA 231.31 USD 2014-09-19 price VHT 121.24 USD 2014-09-19 price GLD 111.33 USD 2014-09-26 price VBMPX 243.11 USD 2014-09-26 price RGAGX 477.67 USD 2014-09-26 price ITOT 120.33 USD 2014-09-26 price VEA 232.01 USD 2014-09-26 price VHT 122.77 USD 2014-09-26 price GLD 108.77 USD 2014-10-03 price VBMPX 237.76 USD 2014-10-03 price RGAGX 478.66 USD 2014-10-03 price ITOT 121.06 USD 2014-10-03 price VEA 231.34 USD 2014-10-03 price VHT 121.64 USD 2014-10-03 price GLD 106.87 USD 2014-10-10 price VBMPX 233.17 USD 2014-10-10 price RGAGX 481.04 USD 2014-10-10 price ITOT 120.82 USD 2014-10-10 price VEA 232.28 USD 2014-10-10 price VHT 123.10 USD 2014-10-10 price GLD 109.66 USD 2014-10-17 price VBMPX 224.68 USD 2014-10-17 price RGAGX 486.12 USD 2014-10-17 price ITOT 121.82 USD 2014-10-17 price VEA 233.86 USD 2014-10-17 price VHT 121.91 USD 2014-10-17 price GLD 111.64 USD 2014-10-24 price VBMPX 214.82 USD 2014-10-24 price RGAGX 481.96 USD 2014-10-24 price ITOT 121.60 USD 2014-10-24 price VEA 235.92 USD 2014-10-24 price VHT 120.25 USD 2014-10-24 price GLD 112.36 USD 2014-10-31 price VBMPX 221.99 USD 2014-10-31 price RGAGX 478.69 USD 2014-10-31 price ITOT 119.55 USD 2014-10-31 price VEA 236.42 USD 2014-10-31 price VHT 122.58 USD 2014-10-31 price GLD 113.99 USD 2014-11-07 price VBMPX 222.84 USD 2014-11-07 price RGAGX 473.84 USD 2014-11-07 price ITOT 121.05 USD 2014-11-07 price VEA 236.31 USD 2014-11-07 price VHT 124.89 USD 2014-11-07 price GLD 117.26 USD 2014-11-14 price VBMPX 229.74 USD 2014-11-14 price RGAGX 469.55 USD 2014-11-14 price ITOT 123.19 USD 2014-11-14 price VEA 235.35 USD 2014-11-14 price VHT 124.48 USD 2014-11-14 price GLD 116.78 USD 2014-11-21 price VBMPX 228.42 USD 2014-11-21 price RGAGX 483.46 USD 2014-11-21 price ITOT 122.38 USD 2014-11-21 price VEA 231.59 USD 2014-11-21 price VHT 126.36 USD 2014-11-21 price GLD 116.50 USD 2014-11-28 price VBMPX 227.99 USD 2014-11-28 price RGAGX 488.86 USD 2014-11-28 price ITOT 120.28 USD 2014-11-28 price VEA 231.35 USD 2014-11-28 price VHT 127.44 USD 2014-11-28 price GLD 121.94 USD 2014-12-05 price VBMPX 229.45 USD 2014-12-05 price RGAGX 491.91 USD 2014-12-05 price ITOT 123.23 USD 2014-12-05 price VEA 231.63 USD 2014-12-05 price VHT 125.51 USD 2014-12-05 price GLD 125.82 USD 2014-12-12 price VBMPX 228.25 USD 2014-12-12 price RGAGX 497.57 USD 2014-12-12 price ITOT 120.80 USD 2014-12-12 price VEA 232.98 USD 2014-12-12 price VHT 125.90 USD 2014-12-12 price GLD 126.52 USD 2014-12-19 price VBMPX 233.19 USD 2014-12-19 price RGAGX 503.67 USD 2014-12-19 price ITOT 122.03 USD 2014-12-19 price VEA 234.34 USD 2014-12-19 price VHT 128.14 USD 2014-12-19 price GLD 129.96 USD 2014-12-26 price VBMPX 233.39 USD 2014-12-26 price RGAGX 507.77 USD 2014-12-26 price ITOT 120.44 USD 2014-12-26 price VEA 232.95 USD 2014-12-26 price VHT 126.81 USD 2014-12-26 price GLD 133.53 USD 2015-01-02 price VBMPX 240.90 USD 2015-01-02 price RGAGX 507.52 USD 2015-01-02 price ITOT 119.06 USD 2015-01-02 price VEA 233.84 USD 2015-01-02 price VHT 128.82 USD 2015-01-02 price GLD 132.70 USD 2015-01-09 price VBMPX 244.58 USD 2015-01-09 price RGAGX 506.06 USD 2015-01-09 price ITOT 118.33 USD 2015-01-09 price VEA 237.27 USD 2015-01-09 price VHT 129.35 USD 2015-01-09 price GLD 129.25 USD 2015-01-16 price VBMPX 246.42 USD 2015-01-16 price RGAGX 509.57 USD 2015-01-16 price ITOT 119.94 USD 2015-01-16 price VEA 234.72 USD 2015-01-16 price VHT 127.21 USD 2015-01-16 price GLD 128.59 USD 2015-01-23 price VBMPX 253.80 USD 2015-01-23 price RGAGX 514.64 USD 2015-01-23 price ITOT 119.99 USD 2015-01-23 price VEA 235.53 USD 2015-01-23 price VHT 129.79 USD 2015-01-23 price GLD 126.47 USD 2015-01-30 price VBMPX 256.81 USD 2015-01-30 price RGAGX 526.63 USD 2015-01-30 price ITOT 121.46 USD 2015-01-30 price VEA 234.82 USD 2015-01-30 price VHT 131.29 USD 2015-01-30 price GLD 124.91 USD 2015-02-06 price VBMPX 259.03 USD 2015-02-06 price RGAGX 523.10 USD 2015-02-06 price ITOT 121.41 USD 2015-02-06 price VEA 237.20 USD 2015-02-06 price VHT 131.82 USD 2015-02-06 price GLD 125.04 USD 2015-02-13 price VBMPX 253.93 USD 2015-02-13 price RGAGX 537.44 USD 2015-02-13 price ITOT 122.72 USD 2015-02-13 price VEA 235.21 USD 2015-02-13 price VHT 130.50 USD 2015-02-13 price GLD 123.63 USD 2015-02-20 price VBMPX 255.14 USD 2015-02-20 price RGAGX 536.30 USD 2015-02-20 price ITOT 123.67 USD 2015-02-20 price VEA 236.80 USD 2015-02-20 price VHT 129.85 USD 2015-02-20 price GLD 126.96 USD 2015-02-27 price VBMPX 260.60 USD 2015-02-27 price RGAGX 546.33 USD 2015-02-27 price ITOT 123.43 USD 2015-02-27 price VEA 236.60 USD 2015-02-27 price VHT 128.58 USD 2015-02-27 price GLD 126.30 USD 2015-03-06 price VBMPX 264.20 USD 2015-03-06 price RGAGX 537.09 USD 2015-03-06 price ITOT 124.74 USD 2015-03-06 price VEA 238.51 USD 2015-03-06 price VHT 128.96 USD 2015-03-06 price GLD 125.65 USD 2015-03-13 price VBMPX 267.60 USD 2015-03-13 price RGAGX 535.79 USD 2015-03-13 price ITOT 124.64 USD 2015-03-13 price VEA 240.08 USD 2015-03-13 price VHT 127.88 USD 2015-03-13 price GLD 127.23 USD 2015-03-20 price VBMPX 272.05 USD 2015-03-20 price RGAGX 533.82 USD 2015-03-20 price ITOT 124.90 USD 2015-03-20 price VEA 239.89 USD 2015-03-20 price VHT 127.87 USD 2015-03-20 price GLD 129.83 USD 2015-03-27 price VBMPX 274.95 USD 2015-03-27 price RGAGX 533.88 USD 2015-03-27 price ITOT 128.36 USD 2015-03-27 price VEA 240.12 USD 2015-03-27 price VHT 130.96 USD 2015-03-27 price GLD 131.29 USD 2015-04-03 price VBMPX 275.32 USD 2015-04-03 price RGAGX 537.74 USD 2015-04-03 price ITOT 131.10 USD 2015-04-03 price VEA 241.68 USD 2015-04-03 price VHT 131.98 USD 2015-04-03 price GLD 133.16 USD 2015-04-10 price VBMPX 278.66 USD 2015-04-10 price RGAGX 544.04 USD 2015-04-10 price ITOT 133.56 USD 2015-04-10 price VEA 241.87 USD 2015-04-10 price VHT 133.12 USD 2015-04-10 price GLD 131.31 USD 2015-04-17 price VBMPX 277.75 USD 2015-04-17 price RGAGX 555.36 USD 2015-04-17 price ITOT 132.89 USD 2015-04-17 price VEA 243.06 USD 2015-04-17 price VHT 132.23 USD 2015-04-17 price GLD 136.38 USD 2015-04-24 price VBMPX 275.00 USD 2015-04-24 price RGAGX 572.58 USD 2015-04-24 price ITOT 133.68 USD 2015-04-24 price VEA 245.60 USD 2015-04-24 price VHT 133.78 USD 2015-04-24 price GLD 138.97 USD 2015-05-01 price VBMPX 271.19 USD 2015-05-01 price RGAGX 575.09 USD 2015-05-01 price ITOT 132.17 USD 2015-05-01 price VEA 247.91 USD 2015-05-01 price VHT 132.65 USD 2015-05-01 price GLD 142.18 USD 2015-05-08 price VBMPX 271.81 USD 2015-05-08 price RGAGX 580.28 USD 2015-05-08 price ITOT 130.67 USD 2015-05-08 price VEA 247.69 USD 2015-05-08 price VHT 131.19 USD 2015-05-08 price GLD 140.93 USD 2015-05-15 price VBMPX 273.84 USD 2015-05-15 price RGAGX 568.97 USD 2015-05-15 price ITOT 133.11 USD 2015-05-15 price VEA 246.27 USD 2015-05-15 price VHT 130.76 USD 2015-05-15 price GLD 141.87 USD 2015-05-22 price VBMPX 266.27 USD 2015-05-22 price RGAGX 567.32 USD 2015-05-22 price ITOT 131.33 USD 2015-05-22 price VEA 245.35 USD 2015-05-22 price VHT 130.08 USD 2015-05-22 price GLD 143.20 USD 2015-05-29 price VBMPX 264.48 USD 2015-05-29 price RGAGX 567.45 USD 2015-05-29 price ITOT 130.80 USD 2015-05-29 price VEA 243.06 USD 2015-05-29 price VHT 132.64 USD 2015-05-29 price GLD 140.93 USD 2015-06-05 price VBMPX 262.37 USD 2015-06-05 price RGAGX 571.21 USD 2015-06-05 price ITOT 129.59 USD 2015-06-05 price VEA 243.51 USD 2015-06-05 price VHT 133.20 USD 2015-06-05 price GLD 137.85 USD 2015-06-12 price VBMPX 264.55 USD 2015-06-12 price RGAGX 585.83 USD 2015-06-12 price ITOT 129.42 USD 2015-06-12 price VEA 244.20 USD 2015-06-12 price VHT 134.57 USD 2015-06-12 price GLD 137.28 USD 2015-06-19 price VBMPX 270.00 USD 2015-06-19 price RGAGX 587.23 USD 2015-06-19 price ITOT 130.25 USD 2015-06-19 price VEA 243.36 USD 2015-06-19 price VHT 133.36 USD 2015-06-19 price GLD 133.58 USD 2015-06-26 price VBMPX 275.91 USD 2015-06-26 price RGAGX 577.69 USD 2015-06-26 price ITOT 133.70 USD 2015-06-26 price VEA 246.79 USD 2015-06-26 price VHT 132.49 USD 2015-06-26 price GLD 133.00 USD 2015-07-03 price VBMPX 273.55 USD 2015-07-03 price RGAGX 583.72 USD 2015-07-03 price ITOT 133.70 USD 2015-07-03 price VEA 249.86 USD 2015-07-03 price VHT 135.67 USD 2015-07-03 price GLD 136.95 USD 2015-07-10 price VBMPX 279.07 USD 2015-07-10 price RGAGX 581.63 USD 2015-07-10 price ITOT 134.19 USD 2015-07-10 price VEA 247.98 USD 2015-07-10 price VHT 134.32 USD 2015-07-10 price GLD 137.14 USD 2015-07-17 price VBMPX 280.15 USD 2015-07-17 price RGAGX 584.10 USD 2015-07-17 price ITOT 135.13 USD 2015-07-17 price VEA 249.62 USD 2015-07-17 price VHT 136.28 USD 2015-07-17 price GLD 137.09 USD 2015-07-24 price VBMPX 280.35 USD 2015-07-24 price RGAGX 584.42 USD 2015-07-24 price ITOT 136.19 USD 2015-07-24 price VEA 250.02 USD 2015-07-24 price VHT 136.73 USD 2015-07-24 price GLD 134.51 USD 2015-07-31 price VBMPX 283.95 USD 2015-07-31 price RGAGX 581.91 USD 2015-07-31 price ITOT 134.19 USD 2015-07-31 price VEA 249.08 USD 2015-07-31 price VHT 137.70 USD 2015-07-31 price GLD 137.54 USD 2015-08-07 price VBMPX 280.17 USD 2015-08-07 price RGAGX 599.78 USD 2015-08-07 price ITOT 135.71 USD 2015-08-07 price VEA 249.37 USD 2015-08-07 price VHT 139.30 USD 2015-08-07 price GLD 136.70 USD 2015-08-14 price VBMPX 279.53 USD 2015-08-14 price RGAGX 596.14 USD 2015-08-14 price ITOT 136.04 USD 2015-08-14 price VEA 249.42 USD 2015-08-14 price VHT 141.12 USD 2015-08-14 price GLD 134.01 USD 2015-08-21 price VBMPX 276.34 USD 2015-08-21 price RGAGX 592.85 USD 2015-08-21 price ITOT 134.45 USD 2015-08-21 price VEA 249.19 USD 2015-08-21 price VHT 141.63 USD 2015-08-21 price GLD 133.62 USD 2015-08-28 price VBMPX 275.77 USD 2015-08-28 price RGAGX 584.66 USD 2015-08-28 price ITOT 135.69 USD 2015-08-28 price VEA 249.68 USD 2015-08-28 price VHT 139.66 USD 2015-08-28 price GLD 134.88 USD 2015-09-04 price VBMPX 277.90 USD 2015-09-04 price RGAGX 587.85 USD 2015-09-04 price ITOT 133.55 USD 2015-09-04 price VEA 248.75 USD 2015-09-04 price VHT 138.46 USD 2015-09-04 price GLD 137.74 USD 2015-09-11 price VBMPX 279.64 USD 2015-09-11 price RGAGX 587.96 USD 2015-09-11 price ITOT 132.81 USD 2015-09-11 price VEA 250.43 USD 2015-09-11 price VHT 134.30 USD 2015-09-11 price GLD 138.60 USD 2015-09-18 price VBMPX 283.22 USD 2015-09-18 price RGAGX 596.62 USD 2015-09-18 price ITOT 134.07 USD 2015-09-18 price VEA 248.30 USD 2015-09-18 price VHT 133.85 USD 2015-09-18 price GLD 139.54 USD 2015-09-25 price VBMPX 286.42 USD 2015-09-25 price RGAGX 587.15 USD 2015-09-25 price ITOT 133.00 USD 2015-09-25 price VEA 252.55 USD 2015-09-25 price VHT 133.13 USD 2015-09-25 price GLD 140.98 USD 2015-10-02 price VBMPX 283.21 USD 2015-10-02 price RGAGX 566.36 USD 2015-10-02 price ITOT 130.51 USD 2015-10-02 price VEA 254.07 USD 2015-10-02 price VHT 134.57 USD 2015-10-02 price GLD 142.49 USD 2015-10-09 price VBMPX 291.70 USD 2015-10-09 price RGAGX 559.80 USD 2015-10-09 price ITOT 132.50 USD 2015-10-09 price VEA 254.34 USD 2015-10-09 price VHT 135.06 USD 2015-10-09 price GLD 145.41 USD * Cash ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/contrib/icon.svg0000644000175000001440000001061600000000000015737 0ustar00jakobusers00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/contrib/pyinstaller.spec0000644000175000001440000000201500000000000017502 0ustar00jakobusers00000000000000# vim: set ft=python: from PyInstaller.utils.hooks import collect_submodules hidden_imports = collect_submodules('beancount', filter=lambda name: 'test' not in name) data_files = [ ('../fava/help', 'fava/help'), ('../fava/static/css', 'fava/static/css'), ('../fava/static/gen', 'fava/static/gen'), ('../fava/templates', 'fava/templates'), ('../fava/translations', 'fava/translations'), ] a = Analysis( ['../fava/cli.py'], pathex=['.'], binaries=None, datas=data_files, hiddenimports=hidden_imports, hookspath=[], runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=None ) pyz = PYZ(a.pure, a.zipped_data, cipher=None) exe = EXE( pyz, a.scripts, exclude_binaries=True, name='cli', debug=False, strip=False, upx=True, console=True, ) coll = COLLECT( exe, a.binaries, a.zipfiles, a.datas, strip=False, upx=True, name='fava', ) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1581856610.6059573 fava-1.14/contrib/pythonanywhere/0000755000175000001440000000000000000000000017346 5ustar00jakobusers00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/contrib/pythonanywhere/README.md0000644000175000001440000000115200000000000020624 0ustar00jakobusers00000000000000Fava has two example pages hosted on [pythonanywhere.com](https://pythonanywhere.com): - [fava.pythonanywhere.com](https://fava.pythonanywhere.com), which runs the latest released version. - [favadev.pythonanywhere.com](https://favadev.pythonanywhere.com), which tracks the HEAD of the GitHub repo ([github.com/beancount/fava](https://github.com/beancount/fava)) There are four parts to both instances: 1. `/home/$USER/update.sh` installs Fava. 2. A cron job that executes this file daily. 3. A WSGI configuration file at `/var/www/fava_pythonanywhere_com_wsgi.py` 4. A web app that uses this file. ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1581856610.6059573 fava-1.14/contrib/pythonanywhere/fava/0000755000175000001440000000000000000000000020263 5ustar00jakobusers00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/contrib/pythonanywhere/fava/fava_pythonanywhere_com_wsgi.py0000644000175000001440000000032600000000000026606 0ustar00jakobusers00000000000000from fava.application import app as application application.config["BEANCOUNT_FILES"] = [ "/home/fava/example.beancount", "/home/fava/budgets-example.beancount", "/home/fava/huge-example.beancount", ] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/contrib/pythonanywhere/fava/update.sh0000644000175000001440000000140300000000000022077 0ustar00jakobusers00000000000000#!/bin/bash # Start virtualenv virtualenv ~/venv --python=python3.5 source ~/venv/bin/activate # Update fava pip install fava --upgrade rm -f ~/example.beancount ~/budgets-example.beancount ~/huge-example.beancount curl -o ~/example.beancount https://raw.githubusercontent.com/beancount/fava/master/contrib/examples/example.beancount curl -o ~/budgets-example.beancount https://raw.githubusercontent.com/beancount/fava/master/contrib/examples/budgets-example.beancount curl -o ~/huge-example.beancount https://raw.githubusercontent.com/beancount/fava/master/contrib/examples/huge-example.beancount chmod 400 ~/example.beancount chmod 400 ~/budgets-example.beancount chmod 400 ~/huge-example.beancount # Reload web page touch /var/www/fava_pythonanywhere_com_wsgi.py ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1581856610.6059573 fava-1.14/contrib/pythonanywhere/favadev/0000755000175000001440000000000000000000000020762 5ustar00jakobusers00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/contrib/pythonanywhere/favadev/favadev_pythonanywhere_com_wsgi.py0000644000175000001440000000033700000000000030006 0ustar00jakobusers00000000000000from fava.application import app as application application.config["BEANCOUNT_FILES"] = [ "/home/favadev/example.beancount", "/home/favadev/budgets-example.beancount", "/home/favadev/huge-example.beancount", ] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/contrib/pythonanywhere/favadev/update.sh0000644000175000001440000000115400000000000022601 0ustar00jakobusers00000000000000#!/bin/bash cd ~/fava || exit git fetch git reset --hard origin/master source /home/favadev/.virtualenvs/fava/bin/activate # Update fava make pip install -e ~/fava # Copy example files rm -f ~/example.beancount ~/budgets-example.beancount ~/huge-example.beancount cp contrib/examples/example.beancount ~/example.beancount cp contrib/examples/budgets-example.beancount ~/budgets-example.beancount cp contrib/examples/huge-example.beancount ~/huge-example.beancount chmod 400 ~/example.beancount ~/budgets-example.beancount ~/huge-example.beancount # Reload web page touch /var/www/favadev_pythonanywhere_com_wsgi.py ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/contrib/scripts.py0000755000175000001440000000653200000000000016334 0ustar00jakobusers00000000000000#!/usr/bin/env python3 """Various utilities.""" import json import os import click import requests from beancount.query import query_env from beancount.query import query_parser from fava import LOCALES BASE_PATH = os.path.normpath( os.path.join(os.path.dirname(__file__), "../fava") ) @click.group() def cli(): """Various utilities.""" def _env_to_list(attributes): for name in attributes.keys(): if isinstance(name, tuple): name = name[0] yield name @cli.command() def generate_bql_grammar_json(): """Generate a JSON file with BQL grammar attributes. The online code editor needs to have the list of available columns, functions, and keywords for syntax highlighting and completion. Should be run whenever the BQL changes.""" target_env = query_env.TargetsEnvironment() data = { "columns": sorted(set(_env_to_list(target_env.columns))), "functions": sorted(set(_env_to_list(target_env.functions))), "keywords": sorted({kw.lower() for kw in query_parser.Lexer.keywords}), } path = os.path.join( os.path.dirname(__file__), "../fava/static/javascript/codemirror/bql-grammar.ts", ) with open(path, "w", encoding="utf-8") as json_file: json_file.write("export default " + json.dumps(data)) @cli.command() def download_translations(): """Fetch updated translations from POEditor.com.""" token = os.environ.get("POEDITOR_TOKEN") if not token: raise click.UsageError( "The POEDITOR_TOKEN environment variable needs to be set." ) for language in LOCALES: download_from_poeditor(language, token) @cli.command() def upload_translations(): """Upload .pot message catalog to POEditor.com.""" token = os.environ.get("POEDITOR_TOKEN") if not token: raise click.UsageError( "The POEDITOR_TOKEN environment variable needs to be set." ) path = os.path.join(BASE_PATH, f"translations/messages.pot") click.echo(f"Uploading message catalog: {path}") data = { "api_token": token, "id": 90283, "updating": "terms", # "sync_terms": 1, } files = {"file": open(path, "rb")} request = requests.post( "https://api.poeditor.com/v2/projects/upload", data=data, files=files ) click.echo("Done: " + str(request.json()["result"]["terms"])) # For these languages, the name on POEDITOR is off. POEDITOR_LANGUAGE_NAME = {"zh": "zh-CN", "zh_Hant_TW": "zh-TW"} def download_from_poeditor(language, token): """Download .po-file from POEditor and save to disk.""" click.echo(f'Downloading .po-file for language "{language}"') poeditor_name = POEDITOR_LANGUAGE_NAME.get(language, language) data = { "api_token": token, "id": 90283, "language": poeditor_name, "type": "po", } request = requests.post( "https://api.poeditor.com/v2/projects/export", data=data ) url = request.json()["result"]["url"] content = requests.get(url).content folder = os.path.join(BASE_PATH, "translations", language, "LC_MESSAGES") if not os.path.exists(folder): os.makedirs(folder) path = os.path.join(folder, f"messages.po") with open(path, "wb") as file_: file_.write(content) click.echo(f'Downloaded to "{path}"') if __name__ == "__main__": cli() ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1581856610.6059573 fava-1.14/docs/0000755000175000001440000000000000000000000013552 5ustar00jakobusers00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/docs/api.rst0000644000175000001440000000027300000000000015057 0ustar00jakobusers00000000000000API Documentation ================= .. note:: There's no stability guarantee as this is just for internal purposes currently. .. toctree:: :glob: api/fava* api/beancount* ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/docs/changelog.rst0000644000175000001440000003136100000000000016237 0ustar00jakobusers00000000000000Changelog ========= Unreleased ---------- Nothing yet. v1.13 (2020-02-01) ------------------ Fava can now display charts for BQL queries - if they have exactly two columns with the first being a date or string and the second an inventory, then a line chart or treemap chart is shown on the query page. v1.12 (2019-12-03) ------------------ Apart from plenty of bug fixes, this release mainly contains improvements to the forms to add transactions: postings can now be dragged and the full cost syntax of Beancount should supported. v1.11 (2019-08-20) ------------------ The import page of Fava has been reworked - it now supports moving files to the documents folder and the import process should be a bit more interactive. This release also contains various fixes and a new `collapse-pattern` option to collapse accounts in account trees based on regular expressions (and replaces the use of the `fava-collapse-account` metadata entry). Other changes: - Command line flags can be specified by setting environment variables. - A Taiwanese translation has been added. v1.10 (2019-01-31) ------------------ This release contains mostly smaller changes and fixes. In particular, the net worth chart will now follow the selected conversion. v1.9 (2018-10-08) ----------------- In this release, the click behaviour has been updated to allow filtering for payees. The entry input forms now allow inputting prices and costs. As always, bugs have been fixed. v1.8 (2018-07-25) ----------------- The journal design has been updated and should now have a clearer structure. Starting with this version, there will not be any more GUI releases of Fava. The GUI broke frequently and does not seem to worth the maintenance burden. Other changes: - When downloading documents, the original filename will be used. - `any()` and `all()` functions have been added to the filter syntax to allow filtering entries by properties of their postings. - As always, bugs have been fixed. v1.7 (2018-03-09) ----------------- The entry filters have been reworked in this release and should now support for more flexible filtering of the entries. See the help page on how the new syntax works. Also, when completing the payee in the transaction form, the postings of the last transaction for this payee will be auto-filled. Other changes: - The fava-option to hide the charts has been removed. This is now tracked in the page URL. - As always, bugs have been fixed. v1.6 (2017-10-06) ----------------- This is a release with various small changes and mainly some speed improvements to the Balance Sheet and the net worth calculation. Also, if 'At Value' is selected, the current unrealized gain is shown in parentheses in the Balance Sheet. Other changes: - The currently filtered entries can now be exported from the Journal page. - The CLI now has a ``--version`` flag. v1.5 (2017-07-23) ----------------- Fava now has an interface to edit single entries. Clicking on the entry date in the Journal will open an overlay that shows the entry context and allows editing just the lines of that entry. Other changes: - The source editor now has a menu that gives access to editor commands like "fold all". - Entries with matching tags or links can now be excluded with ``-#tag``. - The keyboard shortcuts are now displayed in-place. - The ``incognito`` option has been removed and replaced with a ``--incognito`` command line switch. - As always, several bugs have been fixed. v1.4 (2017-05-14) ----------------- Fava now provides an interface for Beancount's import system that allows you to import transactions from your bank for example. Fava can now show your balances at market value or convert them to a single currency if your file contains the necessary price information. We now also provide a compiled GUI version of Fava for Linux and macOS. This version might still be a bit buggy so any feedback/help on it is very welcome. Other changes: - The ``insert-entry`` option can be used to control where transactions are inserted. - The transaction form now accepts tags and links in the narration field. - Budgets are now accumulated over all children where appropriate. - As always, several bugs have been fixed. Thanks to :user:`TZdyrski` and :user:`Akuukis` for their contributions. v1.3 (2017-03-15) ----------------- The translations of Fava are now on `POEditor.com `__, which has helped us get translations in five more languages: Chinese (simplified), Dutch, French, Portuguese, and Spanish. A big thank you to the new translators! The transaction form has been improved, it now supports adding metadata and the suggestions will be ranked by how often and recently they occur (using exponential decay). The Query page supports all commands of the ``bean-query`` shell and shares its history of recently used queries. Fava has gained a basic extension mechanism. Extensions allow you to run hooks at various points, e.g., after adding a transaction. They are specified using the ``extensions`` option and for an example, see the ``fava.ext.auto_commit`` extension. Other changes: - The default sort order in journals has been reversed so that the most recent entries come first. - The new ``incognito`` option can be used to obscure all numbers. - As always, several bugs have been fixed. Thanks to :user:`johannesharms` and :user:`xentac` for their contributions. v1.2 (2016-12-25) ----------------- You can now add transactions from within Fava. The form supports autocompletion for most fields. Fava will now show a little bubble in the sidebar for the number of events in the next week. This can be configured with the ``upcoming-events`` option. Other changes: - The payee filter can filter by regular expression. - The tag filter can filter for links, too. - There's a nice spinning indicator during asynchronous page loads. - The Journal shows little indicators for metadata. - As always, several bugs have been fixed. Thanks to :user:`fokusov` for their contributions. v1.1 (2016-11-19) ----------------- You can now upload documents by dropping them onto transactions, which will also add the file path as `statement` metadata to the transaction. Fava also ships with a plugin to link these transactions with the generated documents. See the help pages for details. This is the first release for which we provide compiled binaries (for macOS and Linux). These do not have any dependencies and can simply be executed from the terminal. Other changes: - The bar charts on account pages now also show budgets. - The Journal can now be sorted by date, flag and narration. - Fava now has a Russian translation, thanks to :user:`fokusov`. - As always, several bugs have been fixed. Thanks to :user:`adamgibbins` and :user:`xentac` for their contributions. v1.0 (2016-10-19) ----------------- This is a major new release that includes too many improvements and changes to list. Some highlights: - The layout has been tweaked and we use some nicer fonts. - Fava looks and works much better on smaller screens. - Fava loads most pages asynchronously, so navigating Fava is much faster and responsive. Fava's configuration is not read from a configuration file anymore but can rather be specified using custom entries in the Beancount file. Some options have also been removed or renamed, so check Fava's help page on the available options when upgrading from v0.3.0. There have been many changes under the hood to improve Fava's codebase and a lot of bugs have been squashed. Thanks to :user:`adamgibbins`, :user:`davidastephens`, :user:`xentac`, and :user:`yegle` for their contributions. v0.3.0 (2016-03-24) ------------------- Additions - Support for switching between multiple beancount files. :bug:`213` - New sunburst charts. :bug:`198` - Add "Clear filter" button when filters are active. :bug:`290` - Simple budgeting functionality in the Account view. See help pages on how to use budgets. :bug:`294` - German translation. :bug:`284` - The Beancount is now being reloaded when it is saved in the Source Editor. - New Journal filter controls. Thanks to :user:`yagebu`. - Tree-tables are now displayed in a hierarchical way. Thanks to :user:`yagebu`. Changes - All charts are now rendered with d3.js. Thanks to :user:`yagebu`. - The title of a page is now shown in the header to save screen space. - Changed shortcut for Journal from ``g g`` to ``g j`` as the Journal was renamed from "General Journal" to "Journal". New configuration options - ``language``: The language to use. Valid languages are ``"en"`` and ``"de"`` (default: ``"en"``). :bug:`284` - ``treemaps-show-negative-numbers`` was removed. Fixes - Commodity prices are now filtered when a Time filter is enabled. :bug:`273` - Some improvements to the help pages. - Many small bug fixes. Thanks to :user:`yagebu`. v0.2.6 (2016-03-20) ------------------- Additions - There are now more interval options available for charts and the account balances report. The interval can be selected from a dropdown next to the charts. :bug:`175` - Show metadata for postings in the Journal. Thanks to :user:`corani`. :bug:`185` - The editor now supports org-mode style folding. Thanks to :user:`corani`. :bug:`209` - Show colored dots for all the postings of a transaction in the Journal report. This way flagged postings can be quickly spotted. :bug:`195` - Add keyboard shortcuts for save to source editor. :bug:`199` Changes - Use beancount's DisplayContext to determine the correct precision at which to render numbers. :bug:`188` - Improve the way that query results are serialized to XLS etc. Thanks to :user:`corani`. :bug:`168` - Show inverse rates for pairs of operating currencies on the commodities report. :bug:`139` - Use Click for the CLI and check if beancount file exists on startup. :bug:`216` - Hide closed accounts in tree tables. Also see new configuration option below. New configuration options - ``editor-strip-trailing-whitespace`` to enable trimming of trailing whitespace in the Source editor (default: "false"). Thanks to :user:`corani`. :bug:`163` - ``show-closed-accounts`` to show closed accounts in tree tables, for example on the balance sheet (default: "false"). :bug:`91` - ``show-accounts-with-zero-balance`` to show accounts with a balance of zero in tree tables (default: "true"). :bug:`91` - ``show-accounts-with-zero-transactions`` to show accounts with no transactions in tree tables (default: "true"). :bug:`91` Fixes - Fixed a bug where the months would be off by one for the interval reports. :bug:`182` - Fix the net worth report for more than one currency. :bug:`207` - Some improvements to the help pages. - Many small bug fixes. v0.2.5 (2016-02-28) ------------------- Bump release to remove unused draft code. v0.2.4 (2016-02-18) ------------------- Additions - Added missing Holdings views compared to ``bean-web``. Thanks to :user:`yagebu`. :bug:`140` - Custom queries are now shown in sidebar. Thanks to :user:`corani`. :bug:`135` - The user settings file is now editable in the Source editor. :bug:`136` - Added second theme. Thanks to Rubén Gómez for the stylesheet. :bug:`59` - Added Help pages. - Query results can now be downloaded as CSV, XLS, XLSX and ODS. :bug:`143` - Documents can now be uploaded by dragging and dropping files over an Account name on the Account page and all tree-tables. :bug:`157` - Journal can now be filtered by transaction type. Thanks to :user:`yagebu`. Changes - The uptodate-indicator is now shown everywhere by default, but only enabled for accounts that have the metadata ``fava-uptodate-indication: "True"`` set on their ``open``-directives. :bug:`35` - Speedier Journal rendering. Thanks to :user:`yagebu`. :bug:`164` - Only basenames will be shown for documents in the Journal. Thanks to :user:`corani`. - Slightly reordered the sidebar menu. - Minor UI tweaks. New configuration options - ``sidebar-show-queries``: The maximum number of custom queries to show in the sidebar (default: 5). - ``theme``: The theme to use. Valid themes are ``"default"`` and ``"alternative"`` (default: ``"default"``). - ``editor-print-margin-column``: Set the column for the print margin in the Source editor (default: 60). :bug:`161` - ``uptodate-indicator-show-everywhere`` (default: "true"). See Changes above. Removed configuration options - ``uptodate-indicator-exclude-accounts``, see Changes above. Fixes - Fixed Net worth calculation. Thanks to :user:`yagebu`. - Many small bug fixes. v0.2.3 (2016-02-15) ------------------- Bumped version to communicate that installing via ``pip install`` now works, all requirements included. Thanks to :user:`blais` and :user:`yagebu`. Earlier Versions ---------------- It was not possible to install any of the earlier versions only using ``pip`` and you may consult the git log for earlier changes. The first commit in the git repository was on December 4th, 2015. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/docs/conf.py0000644000175000001440000000326700000000000015061 0ustar00jakobusers00000000000000# pylint: disable=invalid-name,missing-docstring extensions = [ "sphinx.ext.extlinks", "sphinx.ext.napoleon", "sphinx.ext.autodoc", "sphinx.ext.intersphinx", "sphinx_autodoc_typehints", ] source_suffix = ".rst" master_doc = "index" intersphinx_mapping = {"python": ("https://docs.python.org/3", None)} # General information about the project. project = "Fava" copyright = "2016, Dominik Aumayr" author = "Dominik Aumayr" exclude_patterns = ["_build"] pygments_style = "sphinx" extlinks = { "bug": ("https://github.com/beancount/fava/issues/%s", "#"), "user": ("https://github.com/%s", "@"), } autodoc_default_flags = ["members", "undoc-members"] def skip_namedtuples(_app, _what, _name, obj, _options, _lines): if obj.__doc__ and obj.__doc__.startswith("Alias for field number"): return True return None def setup(app): app.connect("autodoc-skip-member", skip_namedtuples) # Options for HTML output html_theme = "alabaster" html_static_path = ["static"] html_theme_options = { "logo": "logo.png", "logo_name": True, "logo_text_align": "center", "description": 'Web interface for Beancount', "github_user": "beancount", "github_repo": "fava", "github_button": "false", "show_powered_by": "false", "extra_nav_links": { "fava @ GitHub": "https://github.com/beancount/fava", "Chat": "https://gitter.im/beancount/fava", "Issue Tracker": "https://github.com/beancount/fava/issues", }, "link": "#3572b0", "link_hover": "#1A2F59", } # html_static_path = ['_static'] html_sidebars = {"**": ["about.html", "navigation.html"]} htmlhelp_basename = "favadoc" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/docs/development.rst0000644000175000001440000000256400000000000016635 0ustar00jakobusers00000000000000Development =========== If you want to try out the current `master`-version of Fava and see what we are working on, check out the online `dev-demo `__ (updated every midnight UTC). Setting up a development environment ------------------------------------ If you want to hack on Fava or run the latest development version, make sure you have Python 3 (with `pip`) and Node.js (with `npm`) installed. Then this will get you up and running: .. code:: bash git clone https://github.com/beancount/fava.git cd fava # using a virtual environment is optional, but recommended virtualenv -p python3 venv . venv/bin/activate make pip install --editable . You can start Fava in the virtual environment as usual by running ``fava``. You can run the tests with ``make test`` (requires ``tox``). After any changes to the Javascript code, you will need to re-run `make`. If you need a newer version of Beancount than the latest released one, you can install from source like so (more details `here `__): .. code:: bash pip install hg+https://bitbucket.org/blais/beancount#egg=beancount Contributions are very welcome, just open a PR on `GitHub `__. Fava is released under the `MIT License `__. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/docs/generate.py0000644000175000001440000000225500000000000015722 0ustar00jakobusers00000000000000"""Generate the reST-files for the API documentation. sphinx-apidoc is not customizeable enough to do this. """ import os from os import path import pkgutil import beancount import fava MODULES = list( pkgutil.walk_packages(fava.__path__, fava.__name__ + ".") ) + list(pkgutil.walk_packages(beancount.__path__, beancount.__name__ + ".")) RST_PATH = path.join(path.dirname(__file__), "api") if not path.isdir(RST_PATH): os.mkdir(RST_PATH) def heading(name, level="-"): """Return the rst-heading for the given heading.""" return f"{name}\n{level * len(name)}\n\n" for package in ["fava", "beancount"] + [ mod.name for mod in MODULES if mod.ispkg ]: submodules = [ mod.name for mod in MODULES if mod.name.startswith(package) and not mod.ispkg and "_test" not in mod.name and mod.name.count(".") == package.count(".") + 1 ] with open(path.join(RST_PATH, f"{package}.rst"), "w") as rst: rst.write(heading(package, "=")) rst.write(f".. automodule:: {package}\n\n") for submod in submodules: rst.write(heading(submod, "-")) rst.write(f".. automodule:: {submod}\n\n") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/docs/index.rst0000644000175000001440000000124000000000000015410 0ustar00jakobusers00000000000000Welcome to Fava! ================ .. toctree:: :hidden: usage changelog development api Fava is a web interface for the double-entry bookkeeping software `Beancount `__ with a focus on features and usability. You can try out an online `demo `__. .. image:: https://i.imgbox.com/rfb9I7Zw.png If you are new to Fava and Beancount, begin with the :doc:`usage` guide. If you are already familiar with Beancount, this is enough to get you up and running:: pip3 install fava fava ledger.beancount and visit the web interface at `http://localhost:5000 `__. ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1581856610.6059573 fava-1.14/docs/static/0000755000175000001440000000000000000000000015041 5ustar00jakobusers00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/docs/static/logo.png0000644000175000001440000001574100000000000016517 0ustar00jakobusers00000000000000PNG  IHDRmgAMA aIDATx] |Tչ?Β}Ud 2I$}$7J[" RAx}bA\JX|jd%A@BB33af2˹_r{w;{|]1O#{lPF(T^i7Vq\* \Fr%9RMFs7AfMo(SJ4oҫ@Ѷ^ f1!uPoF򕀷e6moӫH!ނ(o 1|Kw/x,p2);J ^Cj!Wd0:z 9a \p5䨆rEa אB: ^Cj!Wd0:z 9! Jh [ݚJG)~GHlI2͂_aO9Iڣ}thw:Rm%Qn2,$-55LMH ʽ0ɂnr+_#Qz+`lݻWF_H2}X&|nZzgdCrrwzO .J0SP))Hݠ~:`֮y`HCyC5F25TX#+f0 páQF4>ӣ\"+D{`F#zǎ}ն?_8 42h2!s(G!h\H%7j*^žo[ӫy:٫i N[K+(\OӇ0mY|r&#l/z uzJPY l OkOh1%Ř=ӧx ؜C,*b܊LO(k%cD٤] 0E!30(nhAƑQoh͍!fg)a<&4)Ч<(XC~Hzt1,R@,3ewFH<@H]5Z 33.;:kA C}PR|\0K~B&whѓצ&zbsSr >8h>:y箎r:uգVέeNENKX-x|=\[y_Vӑ2ߪ~ 6{֗q9]f#) CWOmI,PeLOKkS:jFYhcCDwR!5t: Vl; +4CIo1IScYRq#ObIop>@aP$<|-tZM @0Knq@tѿNJu(mJR4O[+QtWy\1:߉RlB )h~BV'7.Q<09';Y''ue~$&f'+:{ en6o$͈E/oes^F +F{aG>6Q^ԋ-7"]ef en,ZLJ &i[C+6y~Y%{7bԔ-5;G F%6 )=eU:NI,^j5 sfF|;pGL*-*|> $J2jcev/keYCŰm`ټ]iP>: }Űa: N ,K٢ #'p˺Պ;-陻ixCg WO?vY|m8̠%;ޛ)V:0FYTF@໡2܁)ͻ&oCȪmzl![Ln|V D|d G!x6I.]WPGt-uJ:q)诒iD6MmX}DB3E/FU:f1;p$B0{\ J B-`47DϣkD̡2SXUc1Jx0۷ܹD+{O7>KK[mUJw@P.fZ Ohdcɤ[GU@D]!&eEU4Tݕ`f>ҟn9Tp|J an~OcGJx}yc(} 1@&x;D~-C)&m8oOȠŴLV }(yJƩXڮqL0uh8怒(\d-*0 ěAaq`^ܱ$5.6"q*IC)J/NPALʓ&x= 뗨RXԀTC A[>w77c0k=zV**PjDo#j0 V`yZ ( J|tq7c}SFD M$bYR]y=\S5nc9JxswCH*i|8otjf"$Q}xqL簴A wvQR'2#?n_z<99C1<䀠;Eks)ύI"wLhu/j[yMJR7_qC epU<5?za%PȒsՖoy Ò򒦱l.$ĐF:5jZf>a؜*xyA!3ȍC*y=k4g!t|C ђ@բjgс X`}q]nj^yIj>P{Lm0ϣE]-tpֶ1>s:*el;j#4"߳K rA8M|bлsY q4m!uķ[YmNS4k@8up;F mNքR輇leIs+>hFD;}V)RvAFyMn{cQb[KlLcF;8cBsNF,"xl܅̘_wc^H?vD0MWZ0mYVj9W)P"~9r$taW.V߻+N AlހZ6 d6|nlsHWXfK=;꿋sAuynʳ(5n8rdS'N`XWc`P?zHWyGOW D D} xz8f]aϕ7qh{tӰ$~Y"Gᘃ/gn~{EmeƷ#s >k Eҩh.EpQoIىBTOYL 열7Ѓ#07? 2KLIgŭإOv7c//'0wDʿ!Ct`tgl"RRvY]:xQ_Ŋ g y ܈_"_(i#7V(@m>p;=|wqd[Mj8o0 G馞|z9-踇ghWcsA]i&l@p Cz6Gb:|mm۶:=G [B ;JR" C(}U@g i{?rLg7TCs7g#c ~#fYK'٬Pr\0'Nk7{Pl@wLHR._95Jl Mqm9V[9nײ romBj aq؅ͳ c̰:5}OZX3O(Kz*~OBuC ;:Z3Ϲ㑢]kq:ˏ?qPĚJr~[_^A-ɻLD,>;vO)USz+.xطy*j(U85aGլv1*i7@=9wM\ <a>"b^ƼYVE}ޡ&1VMOi >(tR|9񔆳B`IŭUufc}/B_]px8ìO qQ^PȤՂ~8"ZIR]tUBSEru #4kG,iq)EW#^N,+b7S*weez+=~| urT"I9*wv&(-iAWw܍x%{ k 9uPV@W5`t0BT4 U:@R0TUo!tJUN@PUC< )+U9 AU0BT4 U:@R0TUo5i/H$OX}I'0 H:R| X,&~MZ5Ӥ _YmdR5sE?ur#IENDB`././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/docs/usage.rst0000644000175000001440000000365200000000000015416 0ustar00jakobusers00000000000000Getting Started =============== If you're new to Beancount or double-entry accounting in general, we recommend `Command-line Accounting in Context `__, a motivational document written by Martin Blais, the author of Beancount. To learn how to create your beancount file, refer to `Getting Started with Beancount `__ guide. Martin Blais has written a great deal of very detailed documentation for Beancount, see the `Beancount Documentation `__ page for an index of the available documentation. Installation ------------ Fava is known to run on macOS, Linux, and Windows. You will need `Python 3 `__ (at least version 3.5). Then you can use ``pip`` to install Fava by running:: pip3 install fava which will also pull in all required dependencies including Beancount. If you don't have Beancount installed already, you might want to have a look at its `installation instructions `__. If you want to export query results to Microsoft Excel or LibreOffice Calc, use the following command to install the optional dependencies for this feature:: pip3 install fava[excel] Starting Fava ------------- If you installed Fava using ``pip``, start it by running:: fava ledger.beancount pointing it to your Beancount file -- and visit the web interface at `http://localhost:5000 `__. There are some command-line options available, run ``fava --help`` for an overview. For more information on Fava's features, refer to the help pages that are available through Fava's web-interface. Fava comes with Gmail-style keyboard shortcuts; press ``?`` to show an overview. ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1581856610.6092904 fava-1.14/fava/0000755000175000001440000000000000000000000013537 5ustar00jakobusers00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/__init__.py0000644000175000001440000000064400000000000015654 0ustar00jakobusers00000000000000"""Fava – A web interface for Beancount.""" from pkg_resources import get_distribution, DistributionNotFound try: __version__ = get_distribution(__name__).version except DistributionNotFound: # package is not installed pass LOCALES = [ "de", "es", "fa", "fr", "nl", "pt", "ru", "sk", "uk", "zh", "zh_Hant_TW", ] LANGUAGES = [locale[:2] for locale in LOCALES] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/application.py0000644000175000001440000003000600000000000016413 0ustar00jakobusers00000000000000"""Fava's main WSGI application. when using Fava's WSGI app, make sure to set ``app.config['BEANCOUNT_FILES']``. To start a simple server:: from fava.application import app app.config['BEANCOUNT_FILES'] = ['/path/to/file.beancount'] app.run('localhost', 5000) Attributes: app: An instance of :class:`flask.Flask`, this is Fava's WSGI application. """ import datetime import functools import inspect import os.path import threading from io import BytesIO import flask import markdown2 import werkzeug.urls from beancount.core.account import ACCOUNT_RE from beancount.core.data import Document from beancount.utils.text_utils import replace_numbers from flask import abort from flask import Flask from flask import g from flask import redirect from flask import render_template from flask import render_template_string from flask import request from flask import send_file from flask_babel import Babel from flask_babel import get_translations from werkzeug.utils import secure_filename from fava import LANGUAGES from fava import template_filters from fava.core import FavaLedger from fava.core.charts import FavaJSONEncoder from fava.core.helpers import FavaAPIException from fava.help import HELP_PAGES from fava.json_api import json_api from fava.serialisation import serialise from fava.util import resource_path from fava.util import send_file_inline from fava.util import setup_logging from fava.util import slugify from fava.util.date import Interval from fava.util.excel import HAVE_EXCEL setup_logging() app = Flask( # pylint: disable=invalid-name __name__, template_folder=resource_path("templates"), static_folder=resource_path("static"), ) app.register_blueprint(json_api, url_prefix="//api") app.json_encoder = FavaJSONEncoder app.jinja_options["extensions"].append("jinja2.ext.do") app.jinja_options["extensions"].append("jinja2.ext.loopcontrols") app.jinja_env.trim_blocks = True app.jinja_env.lstrip_blocks = True app.config["HAVE_EXCEL"] = HAVE_EXCEL app.config["HELP_PAGES"] = HELP_PAGES app.config["ACCOUNT_RE"] = ACCOUNT_RE REPORTS = [ "_context", "balance_sheet", "commodities", "documents", "events", "editor", "errors", "holdings", "import", "income_statement", "journal", "options", "query", "statistics", "trial_balance", ] LOAD_FILE_LOCK = threading.Lock() def _load_file(): """Load Beancount files. This is run automatically on the first request. """ app.config["LEDGERS"] = {} for filepath in app.config["BEANCOUNT_FILES"]: ledger = FavaLedger(filepath) slug = slugify(ledger.options["title"]) if not slug: slug = slugify(filepath) app.config["LEDGERS"][slug] = ledger app.config["FILE_SLUGS"] = list(app.config["LEDGERS"].keys()) BABEL = Babel(app) @BABEL.localeselector def get_locale(): """Get locale. Returns: The locale that should be used for Babel. If not given as an option to Fava, guess from browser. """ if g.ledger.fava_options["language"]: return g.ledger.fava_options["language"] return request.accept_languages.best_match(["en"] + LANGUAGES) for _, function in inspect.getmembers(template_filters, inspect.isfunction): app.add_template_filter(function) app.add_template_filter(serialise) @app.url_defaults def _inject_filters(endpoint, values): if "bfile" not in values and app.url_map.is_endpoint_expecting( endpoint, "bfile" ): values["bfile"] = g.beancount_file_slug if endpoint in ["static", "index"]: return if "interval" not in values: values["interval"] = request.args.get("interval") if "conversion" not in values: values["conversion"] = request.args.get("conversion") for filter_name in ["account", "filter", "time"]: if filter_name not in values: values[filter_name] = g.filters[filter_name] @app.template_global() def static_url(**values): """Return a static url with an mtime query string for cache busting.""" filename = values.get("filename", None) if not filename: return url_for("static", **values) file_path = os.path.join(app.static_folder, filename) if os.path.exists(file_path): values["mtime"] = int(os.stat(file_path).st_mtime) return url_for("static", **values) app.add_template_global(datetime.date.today, "today") CACHED_URL_FOR = functools.lru_cache(2048)(flask.url_for) @app.template_global() def url_for(endpoint, **values): """A wrapper around flask.url_for that uses a cache.""" _inject_filters(endpoint, values) return CACHED_URL_FOR(endpoint, **values) @app.template_global() def url_for_current(**kwargs): """URL for current page with updated request args.""" if not kwargs: return url_for(request.endpoint, **request.view_args) args = request.view_args.copy() args.update(kwargs) return url_for(request.endpoint, **args) @app.template_global() def url_for_source(**kwargs): """URL to source file (possibly link to external editor).""" if g.ledger.fava_options["use-external-editor"]: return "beancount://{}?lineno={}".format( kwargs.get("file_path"), kwargs.get("line", 1) ) return url_for("report", report_name="editor", **kwargs) @app.context_processor def template_context(): """Inject variables into the template context.""" # pylint: disable=protected-access catalog = get_translations()._catalog return dict(ledger=g.ledger, translations=catalog) @app.before_request def _perform_global_filters(): g.filters = { name: request.args.get(name) for name in ["account", "filter", "time"] } # check (and possibly reload) source file if request.blueprint != "json_api": g.ledger.changed() g.ledger.filter(**g.filters) @app.after_request def _incognito(response): """Replace all numbers with 'X'.""" if app.config.get("INCOGNITO") and response.content_type.startswith( "text/html" ): is_editor = ( request.endpoint == "report" and request.view_args["report_name"] == "editor" ) if not is_editor: original_text = response.get_data(as_text=True) response.set_data(replace_numbers(original_text)) return response @app.url_value_preprocessor def _pull_beancount_file(_, values): g.beancount_file_slug = values.pop("bfile", None) if values else None with LOAD_FILE_LOCK: if not app.config.get("LEDGERS"): _load_file() if not g.beancount_file_slug: g.beancount_file_slug = app.config["FILE_SLUGS"][0] if g.beancount_file_slug not in app.config["FILE_SLUGS"]: abort(404) g.ledger = app.config["LEDGERS"][g.beancount_file_slug] g.conversion = request.args.get( "conversion", g.ledger.fava_options["conversion"] ) g.partial = request.args.get("partial", False) g.interval = Interval.get( request.args.get("interval", g.ledger.fava_options["interval"]) ) @app.errorhandler(FavaAPIException) def fava_api_exception(error): """Handle API errors.""" if g.partial: return error.message, 400 return render_template("_error.html", error=error), 400 @app.route("/") @app.route("//") def index(): """Redirect to the Income Statement (of the given or first file).""" return redirect(url_for("report", report_name="income_statement")) @app.route("//account//") @app.route("//account///") def account(name, subreport="journal"): """The account report.""" if subreport in ["journal", "balances", "changes"]: return render_template( "account.html", account_name=name, subreport=subreport ) abort(404) return None @app.route("//document/", methods=["GET"]) def document(): """Download a document.""" filename = request.args.get("filename") filenames = [ document.filename for document in g.ledger.all_entries_by_type[Document] ] import_directories = [ g.ledger.join_path(d) for d in g.ledger.fava_options["import-dirs"] ] if filename in filenames: return send_file_inline(filename) if any(filename.startswith(d) for d in import_directories): return send_file_inline(filename) return abort(404) @app.route("//statement/", methods=["GET"]) def statement(): """Download a statement file.""" entry_hash = request.args.get("entry_hash") key = request.args.get("key") document_path = g.ledger.statement_path(entry_hash, key) return send_file_inline(document_path) @app.route("//holdings/by_/") def holdings_by(aggregation_key): """The holdings report.""" if aggregation_key in ["account", "currency", "cost_currency"]: return render_template( "_layout.html", active_page="holdings", aggregation_key=aggregation_key, ) return abort(404) @app.route("//_context/") def context(): """Entry context.""" return render_template("_context.html") @app.route("//_query_result/") def query_result(): """Query shell.""" return render_template("_query_result.html") @app.route("///") def report(report_name): """Endpoint for most reports.""" if report_name in REPORTS: return render_template("_layout.html", active_page=report_name) return abort(404) @app.route("//extension//") def extension_report(report_name): """Endpoint for extension reports.""" try: template, extension = g.ledger.extensions.template_and_extension( report_name ) content = render_template_string(template, extension=extension) return render_template( "_layout.html", content=content, page_title=extension.report_title ) except LookupError: return abort(404) @app.route("//download-query/query_result.") def download_query(result_format): """Download a query result.""" name, data = g.ledger.query_shell.query_to_file( request.args.get("query_string", ""), result_format ) filename = "{}.{}".format(secure_filename(name.strip()), result_format) return send_file(data, as_attachment=True, attachment_filename=filename) @app.route("//download-journal/") def download_journal(): """Download a Journal file.""" now = datetime.datetime.now().replace(microsecond=0) filename = "journal_{}.beancount".format(now.isoformat()) data = BytesIO(bytes(render_template("beancount_file"), "utf8")) return send_file(data, as_attachment=True, attachment_filename=filename) @app.route("//help/") @app.route("//help//") def help_page(page_slug="_index"): """Fava's included documentation.""" if page_slug not in app.config["HELP_PAGES"]: abort(404) html = markdown2.markdown_path( os.path.join(resource_path("help"), page_slug + ".md"), extras=["fenced-code-blocks", "tables"], ) return render_template( "_layout.html", active_page="help", page_slug=page_slug, help_html=render_template_string(html), ) @app.route("/jump") def jump(): """Redirect back to the referer, replacing some parameters. This is useful for sidebar links, e.g. a link ``/jump?time=year`` would set the time filter to `year` on the current page. When accessing ``/jump?param1=abc`` from ``/example/page?param1=123¶m2=456``, this view should redirect to ``/example/page?param1=abc¶m2=456``. """ url = werkzeug.urls.url_parse(request.referrer) qs_dict = url.decode_query() for key, values in request.args.lists(): if len(values) == 1 and values[0] == "": try: del qs_dict[key] except KeyError: pass continue qs_dict.setlist(key, values) redirect_url = url.replace( query=werkzeug.urls.url_encode(qs_dict, sort=True) ) return redirect(werkzeug.urls.url_unparse(redirect_url)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1581494646.0 fava-1.14/fava/cli.py0000644000175000001440000000633100000000000014663 0ustar00jakobusers00000000000000"""The command-line interface for Fava.""" import os import errno import click from werkzeug.middleware.profiler import ProfilerMiddleware from werkzeug.middleware.dispatcher import DispatcherMiddleware from cheroot import wsgi from fava.application import app from fava.util import simple_wsgi from fava import __version__ # pylint: disable=too-many-arguments @click.command(context_settings=dict(auto_envvar_prefix="FAVA")) @click.argument( "filenames", nargs=-1, type=click.Path(exists=True, dir_okay=False, resolve_path=True), ) @click.option( "-p", "--port", type=int, default=5000, show_default=True, metavar="", help="The port to listen on.", ) @click.option( "-H", "--host", type=str, default="localhost", show_default=True, metavar="", help="The host to listen on.", ) @click.option("--prefix", type=str, help="Set an URL prefix.") @click.option( "--incognito", is_flag=True, help="Run in incognito mode and obscure all numbers.", ) @click.option("-d", "--debug", is_flag=True, help="Turn on debugging.") @click.option( "--profile", is_flag=True, help="Turn on profiling. Implies --debug." ) @click.option( "--profile-dir", type=click.Path(), help="Output directory for profiling data.", ) @click.version_option(version=__version__, prog_name="fava") def main( filenames, port, host, prefix, incognito, debug, profile, profile_dir ): """Start Fava for FILENAMES on http://:. If the `BEANCOUNT_FILE` environment variable is set, Fava will use the files (space-delimited) specified there in addition to FILENAMES. Note you can also specify command-line options via environment variables. For example, `--host=0.0.0.0` is equivalent to setting the environment variable `FAVA_HOST=0.0.0.0`. """ if profile: # pragma: no cover debug = True env_filename = os.environ.get("BEANCOUNT_FILE") if env_filename: filenames = filenames + tuple(env_filename.split()) if not filenames: raise click.UsageError("No file specified") app.config["BEANCOUNT_FILES"] = filenames app.config["INCOGNITO"] = incognito if prefix: app.wsgi_app = DispatcherMiddleware( simple_wsgi, {prefix: app.wsgi_app} ) if not debug: server = wsgi.Server((host, port), app) print("Running Fava on http://{}:{}".format(host, port)) server.safe_start() else: if profile: app.config["PROFILE"] = True app.wsgi_app = ProfilerMiddleware( app.wsgi_app, restrictions=(30,), profile_dir=profile_dir if profile_dir else None, ) app.jinja_env.auto_reload = True try: app.run(host, port, debug) except OSError as error: if error.errno == errno.EADDRINUSE: raise click.UsageError( "Can not start webserver because the port is already in " "use. Please choose another port with the '-p' option." ) raise # needed for pyinstaller: if __name__ == "__main__": # pragma: no cover main() # pylint: disable=no-value-for-parameter ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1581856610.6126237 fava-1.14/fava/core/0000755000175000001440000000000000000000000014467 5ustar00jakobusers00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/core/__init__.py0000644000175000001440000004237500000000000016613 0ustar00jakobusers00000000000000"""This module provides the data required by Fava's reports.""" import collections import copy import datetime import os from beancount import loader from beancount.core import getters, interpolate, prices, realization from beancount.core.flags import FLAG_UNREALIZED from beancount.core.account_types import get_account_sign from beancount.core.compare import hash_entry from beancount.core.data import ( get_entry, iter_entry_dates, Open, Close, Balance, TxnPosting, Transaction, Document, Event, Custom, ) from beancount.parser.options import get_account_types from beancount.utils.encryption import is_encrypted_file from beancount.utils.misc_utils import filter_type from fava.util import date, pairwise from fava.core.attributes import AttributesModule from fava.core.budgets import BudgetModule from fava.core.charts import ChartModule from fava.core.extensions import ExtensionModule from fava.core.fava_options import parse_options from fava.core.file import FileModule, get_entry_slice from fava.core.filters import AccountFilter, AdvancedFilter, TimeFilter from fava.core.helpers import FavaAPIException from fava.core.ingest import IngestModule from fava.core.misc import FavaMisc from fava.core.number import DecimalFormatModule from fava.core.query_shell import QueryShell from fava.core.tree import Tree from fava.core.watcher import Watcher MAXDATE = datetime.date.max class AccountData: """Holds information about an account.""" __slots__ = ("meta", "close_date") def __init__(self): #: The date on which this account is closed (or datetime.date.max). self.close_date = MAXDATE #: The metadata of the Open entry of this account. self.meta = {} class _AccountDict(dict): """Account info dictionary.""" EMPTY = AccountData() def __missing__(self, key): return self.EMPTY def setdefault(self, key): if key not in self: self[key] = AccountData() return self[key] MODULES = [ "attributes", "budgets", "charts", "extensions", "file", "format_decimal", "misc", "query_shell", "ingest", ] # pylint: disable=too-many-instance-attributes,too-many-public-methods class FavaLedger: """Create an interface for a Beancount ledger. Arguments: path: Path to the main Beancount file. """ __slots__ = [ "account_types", "accounts", "all_entries", "all_entries_by_type", "all_root_account", "beancount_file_path", "_date_first", "_date_last", "entries", "errors", "fava_options", "_filters", "_is_encrypted", "options", "price_map", "root_account", "root_tree", "_watcher", ] + MODULES def __init__(self, path): #: The path to the main Beancount file. self.beancount_file_path = path self._is_encrypted = is_encrypted_file(path) self._filters = {} #: An :class:`AttributesModule` instance. self.attributes = AttributesModule(self) #: A :class:`.BudgetModule` instance. self.budgets = BudgetModule(self) #: A :class:`.ChartModule` instance. self.charts = ChartModule(self) #: A :class:`.ExtensionModule` instance. self.extensions = ExtensionModule(self) #: A :class:`.FileModule` instance. self.file = FileModule(self) #: A :class:`.IngestModule` instance. self.ingest = IngestModule(self) #: A :class:`.FavaMisc` instance. self.misc = FavaMisc(self) #: A :class:`.DecimalFormatModule` instance. self.format_decimal = DecimalFormatModule(self) #: A :class:`.QueryShell` instance. self.query_shell = QueryShell(self) self._watcher = Watcher() #: List of all (unfiltered) entries. self.all_entries = None #: Dict of list of all (unfiltered) entries by type. self.all_entries_by_type = None #: A list of all errors reported by Beancount. self.errors = None #: A Beancount options map. self.options = None #: A Namedtuple containing the names of the five base accounts. self.account_types = None #: A dict containing information about the accounts. self.accounts = _AccountDict() #: A dict with all of Fava's option values. self.fava_options = None self.load_file() def load_file(self): """Load the main file and all included files and set attributes.""" # use the internal function to disable cache if not self._is_encrypted: # pylint: disable=protected-access self.all_entries, self.errors, self.options = loader._load( [(self.beancount_file_path, True)], None, None, None ) else: self.all_entries, self.errors, self.options = loader.load_file( self.beancount_file_path ) self.account_types = get_account_types(self.options) self.price_map = prices.build_price_map(self.all_entries) self.all_root_account = realization.realize( self.all_entries, self.account_types ) entries_by_type = collections.defaultdict(list) for entry in self.all_entries: entries_by_type[type(entry)].append(entry) self.all_entries_by_type = entries_by_type self.accounts = _AccountDict() for entry in entries_by_type[Open]: self.accounts.setdefault(entry.account).meta = entry.meta for entry in entries_by_type[Close]: self.accounts.setdefault(entry.account).close_date = entry.date self.fava_options, errors = parse_options(entries_by_type[Custom]) self.errors.extend(errors) if not self._is_encrypted: self._watcher.update(*self.paths_to_watch()) for mod in MODULES: getattr(self, mod).load_file() self._filters = { "account": AccountFilter(self.options, self.fava_options), "filter": AdvancedFilter(self.options, self.fava_options), "time": TimeFilter(self.options, self.fava_options), } self.filter(True) # pylint: disable=attribute-defined-outside-init def filter(self, force=False, **kwargs): """Set and apply (if necessary) filters.""" changed = False for filter_name, value in kwargs.items(): if self._filters[filter_name].set(value): changed = True if not (changed or force): return self.entries = self.all_entries for filter_class in self._filters.values(): self.entries = filter_class.apply(self.entries) self.root_account = realization.realize( self.entries, self.account_types ) self.root_tree = Tree(self.entries) self._date_first, self._date_last = getters.get_min_max_dates( self.entries, (Transaction) ) if self._date_last: self._date_last = self._date_last + datetime.timedelta(1) if self._filters["time"]: self._date_first = self._filters["time"].begin_date self._date_last = self._filters["time"].end_date @property def end_date(self): """The date to use for prices.""" if self._filters["time"]: return self._filters["time"].end_date return None def join_path(self, *args): """Path relative to the directory of the ledger.""" include_path = os.path.dirname(self.beancount_file_path) return os.path.normpath(os.path.join(include_path, *args)) def paths_to_watch(self): """The paths to included files and document directories. Returns: A tuple (files, directories). """ files = list(self.options["include"]) if self.fava_options["import-config"]: files.append(self.ingest.module_path) return ( files, [ self.join_path(path, account) for account in self.account_types for path in self.options["documents"] ], ) def changed(self): """Check if the file needs to be reloaded. Returns: True if a change in one of the included files or a change in a document folder was detected and the file has been reloaded. """ # We can't reload an encrypted file, so act like it never changes. if self._is_encrypted: return False changed = self._watcher.check() if changed: self.load_file() return changed def interval_ends(self, interval): """Generator yielding dates corresponding to interval boundaries.""" if not self._date_first: return [] return date.interval_ends(self._date_first, self._date_last, interval) def get_account_sign(self, account_name): """Get account sign. Arguments: account_name: An account name. Returns: The sign of the given account, +1 for an assets or expenses account, -1 otherwise. """ return get_account_sign(account_name, self.account_types) @property def root_tree_closed(self): """A root tree for the balance sheet.""" tree = Tree(self.entries) tree.cap(self.options, self.fava_options["unrealized"]) return tree def interval_balances(self, interval, account_name, accumulate=False): """Balances by interval. Arguments: interval: An interval. account_name: An account name. accumulate: A boolean, ``True`` if the balances for an interval should include all entries up to the end of the interval. Returns: A list of RealAccount instances for all the intervals. """ min_accounts = [ account for account in self.accounts.keys() if account.startswith(account_name) ] interval_tuples = list( reversed(list(pairwise(self.interval_ends(interval)))) ) interval_balances = [ realization.realize( list( iter_entry_dates( self.entries, datetime.date.min if accumulate else begin_date, end_date, ) ), min_accounts, ) for begin_date, end_date in interval_tuples ] return interval_balances, interval_tuples def account_journal(self, account_name, with_journal_children=False): """Journal for an account. Args: account_name: An account name. with_journal_children: Whether to include postings of subaccounts of the given account. Returns: A list of tuples ``(entry, postings, change, balance)``. change and balance have already been reduced to units. """ real_account = realization.get_or_create( self.root_account, account_name ) if with_journal_children: postings = realization.get_postings(real_account) else: postings = real_account.txn_postings return [ (entry, postings_, copy.copy(change), copy.copy(balance)) for ( entry, postings_, change, balance, ) in realization.iterate_with_balance(postings) ] @property def documents(self): """All currently filtered documents.""" return list(filter_type(self.entries, Document)) def events(self, event_type=None): """List events (possibly filtered by type).""" events = filter_type(self.entries, Event) if event_type: return [event for event in events if event.type == event_type] return list(events) def get_entry(self, entry_hash): """Find an entry. Arguments: entry_hash: Hash of the entry. Returns: The entry with the given hash. Raises: FavaAPIException: If there is no entry for the given hash. """ try: return next( entry for entry in self.all_entries if entry_hash == hash_entry(entry) ) except StopIteration: raise FavaAPIException( 'No entry found for hash "{}"'.format(entry_hash) ) def context(self, entry_hash): """Context for an entry. Arguments: entry_hash: Hash of entry. Returns: A tuple ``(entry, balances, source_slice, sha256sum)`` of the (unique) entry with the given ``entry_hash``. If the entry is a Balance or Transaction then ``balances`` is a 2-tuple containing the balances before and after the entry of the affected accounts. """ entry = self.get_entry(entry_hash) balances = None if isinstance(entry, (Balance, Transaction)): balances = interpolate.compute_entry_context( self.all_entries, entry ) source_slice, sha256sum = get_entry_slice(entry) return entry, balances, source_slice, sha256sum def commodity_pairs(self): """List pairs of commodities. Returns: A list of pairs of commodities. Pairs of operating currencies will be given in both directions not just in the one found in file. """ fw_pairs = self.price_map.forward_pairs bw_pairs = [] for currency_a, currency_b in fw_pairs: if ( currency_a in self.options["operating_currency"] and currency_b in self.options["operating_currency"] ): bw_pairs.append((currency_b, currency_a)) return sorted(fw_pairs + bw_pairs) def prices(self, base, quote): """List all prices.""" all_prices = prices.get_all_prices(self.price_map, (base, quote)) if self._filters["time"]: return [ (date, price) for date, price in all_prices if self._filters["time"].begin_date <= date < self._filters["time"].end_date ] return all_prices def last_entry(self, account_name): """Get last entry of an account. Args: account_name: An account name. Returns: The last entry of the account if it is not a Close entry. """ account = realization.get_or_create( self.all_root_account, account_name ) last = realization.find_last_active_posting(account.txn_postings) if last is None or isinstance(last, Close): return None return get_entry(last) @property def postings(self): """All postings contained in some transaction.""" return [ posting for entry in filter_type(self.entries, Transaction) for posting in entry.postings ] def statement_path(self, entry_hash, metadata_key): """Returns the path for a statement found in the specified entry.""" entry = self.get_entry(entry_hash) value = entry.meta[metadata_key] paths = [os.path.join(os.path.dirname(entry.meta["filename"]), value)] paths.extend( [ self.join_path( document_root, *posting.account.split(":"), value ) for posting in entry.postings for document_root in self.options["documents"] ] ) for path in paths: if os.path.isfile(path): return path raise FavaAPIException("Statement not found.") def account_uptodate_status(self, account_name): """Status of the last balance or transaction. Args: account_name: An account name. Returns: A status string for the last balance or transaction of the account. - 'green': A balance check that passed. - 'red': A balance check that failed. - 'yellow': Not a balance check. """ real_account = realization.get_or_create( self.all_root_account, account_name ) for txn_posting in reversed(real_account.txn_postings): if isinstance(txn_posting, Balance): if txn_posting.diff_amount: return "red" return "green" if ( isinstance(txn_posting, TxnPosting) and txn_posting.txn.flag != FLAG_UNREALIZED ): return "yellow" return None def account_is_closed(self, account_name): """Check if the account is closed. Args: account_name: An account name. Returns: True if the account is closed before the end date of the current time filter. """ if self._filters["time"]: return self.accounts[account_name].close_date < self._date_last return self.accounts[account_name].close_date is not MAXDATE ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/core/attributes.py0000644000175000001440000000454500000000000017237 0ustar00jakobusers00000000000000"""Attributes for auto-completion.""" from beancount.core import getters from beancount.core.data import Transaction from fava.core.helpers import FavaModule from fava.util.ranking import ExponentialDecayRanker class AttributesModule(FavaModule): """Some attributes of the ledger (mostly for auto-completion).""" def __init__(self, ledger): super().__init__(ledger) self.accounts = None self.currencies = None self.payees = None self.links = None self.tags = None self.years = None def load_file(self): all_entries = self.ledger.all_entries self.links = getters.get_all_links(all_entries) self.tags = getters.get_all_tags(all_entries) self.years = list(getters.get_active_years(all_entries))[::-1] account_ranker = ExponentialDecayRanker( sorted(self.ledger.accounts.keys()) ) currency_ranker = ExponentialDecayRanker( self.ledger.options["commodities"] ) payee_ranker = ExponentialDecayRanker() transactions = self.ledger.all_entries_by_type[Transaction] for txn in transactions: if txn.payee: payee_ranker.update(txn.payee, txn.date) for posting in txn.postings: account_ranker.update(posting.account, txn.date) currency_ranker.update(posting.units.currency, txn.date) if posting.cost: currency_ranker.update(posting.cost.currency, txn.date) self.accounts = account_ranker.sort() self.currencies = currency_ranker.sort() self.payees = payee_ranker.sort() def payee_accounts(self, payee): """Rank accounts for the given payee.""" account_ranker = ExponentialDecayRanker(self.accounts) transactions = self.ledger.all_entries_by_type[Transaction] for txn in transactions: if txn.payee == payee: for posting in txn.postings: account_ranker.update(posting.account, txn.date) return account_ranker.sort() def payee_transaction(self, payee): """The last transaction for the given payee.""" transactions = self.ledger.all_entries_by_type[Transaction] for txn in reversed(transactions): if txn.payee == payee: return txn return None ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/core/budgets.py0000644000175000001440000001160500000000000016501 0ustar00jakobusers00000000000000"""Parsing and computing budgets.""" from collections import defaultdict, namedtuple, Counter from beancount.core.data import Custom from beancount.core.number import Decimal from fava.util.date import ( Interval, days_in_daterange, number_of_days_in_period, ) from fava.core.helpers import FavaModule Budget = namedtuple("Budget", "account date_start period number currency") BudgetError = namedtuple("BudgetError", "source message entry") class BudgetModule(FavaModule): """Parses budget entries.""" def __init__(self, ledger): super().__init__(ledger) self.budget_entries = None def load_file(self): self.budget_entries, errors = parse_budgets( self.ledger.all_entries_by_type[Custom] ) self.ledger.errors.extend(errors) def calculate(self, account_name, begin_date, end_date): """Calculate the budget for an account in an interval.""" return calculate_budget( self.budget_entries, account_name, begin_date, end_date ) def calculate_children(self, account_name, begin_date, end_date): """Calculate the budget for an account including its children.""" return calculate_budget_children( self.budget_entries, account_name, begin_date, end_date ) def __bool__(self): return bool(self.budget_entries) def parse_budgets(custom_entries): """Parse budget directives from custom entries. Args: custom_entries: the Custom entries to parse budgets from. Returns: A dict of accounts to lists of budgets. Example: 2015-04-09 custom "budget" Expenses:Books "monthly" 20.00 EUR """ budgets = defaultdict(list) errors = [] interval_map = { "daily": Interval.DAY, "weekly": Interval.WEEK, "monthly": Interval.MONTH, "quarterly": Interval.QUARTER, "yearly": Interval.YEAR, } for entry in custom_entries: if entry.type == "budget": try: interval = interval_map.get(str(entry.values[1].value)) if not interval: errors.append( BudgetError( entry.meta, "Invalid interval for budget entry", entry, ) ) continue budget = Budget( entry.values[0].value, entry.date, interval, entry.values[2].value.number, entry.values[2].value.currency, ) budgets[budget.account].append(budget) except (IndexError, TypeError): errors.append( BudgetError( entry.meta, "Failed to parse budget entry", entry ) ) return budgets, errors def _matching_budgets(budgets, account_name, date_active): """Find matching budgets. Returns: The budget that is active on the specified date for the specified account. """ last_seen_budgets = {} for budget in budgets[account_name]: if budget.date_start <= date_active: last_seen_budgets[budget.currency] = budget else: break return last_seen_budgets def calculate_budget(budgets, account_name, date_from, date_to): """Calculate budget for an account. Args: budgets: A list of :class:`Budget` entries. account_name: An account name. date_from: Starting date. date_to: End date (exclusive). Returns: A dictionary of currency to Decimal with the budget for the specified account and period. """ if account_name not in budgets: return {} currency_dict = defaultdict(Decimal) for single_day in days_in_daterange(date_from, date_to): matches = _matching_budgets(budgets, account_name, single_day) for budget in matches.values(): currency_dict[budget.currency] += ( budget.number / number_of_days_in_period(budget.period, single_day) ) return currency_dict def calculate_budget_children(budgets, account_name, date_from, date_to): """Calculate budget for an account including budgets of its children. Args: budgets: A list of :class:`Budget` entries. account_name: An account name. date_from: Starting date. date_to: End date (exclusive). Returns: A dictionary of currency to Decimal with the budget for the specified account and period. """ currency_dict = Counter() for account in budgets.keys(): if account.startswith(account_name): currency_dict.update( calculate_budget(budgets, account, date_from, date_to) ) return currency_dict ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/core/charts.py0000644000175000001440000001571200000000000016333 0ustar00jakobusers00000000000000"""Provide data suitable for Fava's charts. """ import datetime from beancount.core import flags from beancount.core import realization from beancount.core.amount import Amount from beancount.core.data import iter_entry_dates from beancount.core.data import Transaction from beancount.core.number import Decimal from beancount.core.inventory import Inventory from beancount.core.position import Position from beancount.utils.misc_utils import filter_type from flask.json import JSONEncoder from fava.core.helpers import FavaAPIException from fava.core.helpers import FavaModule from fava.core.inventory import CounterInventory from fava.core.tree import Tree from fava.template_filters import cost_or_value from fava.template_filters import units from fava.util import listify from fava.util import pairwise class FavaJSONEncoder(JSONEncoder): """Allow encoding some Beancount date structures.""" def default(self, o): # pylint: disable=method-hidden if isinstance(o, Decimal): return float(o) if isinstance(o, (datetime.date, Amount, Position)): return str(o) if isinstance(o, (set, frozenset)): return list(o) try: return JSONEncoder.default(self, o) except TypeError: return str(o) def inv_to_dict(inventory): """Convert an inventory to a simple cost->number dict.""" return {pos.units.currency: pos.units.number for pos in inventory} class ChartModule(FavaModule): """Return data for the various charts in Fava.""" __slots__ = ["ledger"] def events(self, event_type=None): """All events for a given event type.""" return [ { "type": entry.type, "date": entry.date, "description": entry.description, } for entry in self.ledger.events(event_type) ] def hierarchy(self, account_name, begin=None, end=None): """An account tree.""" if begin: tree = Tree(iter_entry_dates(self.ledger.entries, begin, end)) else: tree = self.ledger.root_tree return tree.get(account_name).serialise(end) @listify def prices(self): """The prices for all commodity pairs. Returns: A list of tuples (base, quote, prices) where prices is a list of prices. """ for base, quote in self.ledger.commodity_pairs(): prices = self.ledger.prices(base, quote) if prices: yield base, quote, prices @listify def interval_totals(self, interval, accounts): """Renders totals for account (or accounts) in the intervals. Args: interval: An interval. accounts: A single account (str) or a tuple of accounts. """ for begin, end in pairwise(self.ledger.interval_ends(interval)): inventory = CounterInventory() entries = iter_entry_dates(self.ledger.entries, begin, end) for entry in filter_type(entries, Transaction): for posting in entry.postings: if posting.account.startswith(accounts): inventory.add_position(posting) yield { "date": begin, "balance": cost_or_value(inventory, end), "budgets": self.ledger.budgets.calculate_children( accounts, begin, end ), } @listify def linechart(self, account_name): """The balance of an account. Args: account_name: A string. Returns: A list of dicts for all dates on which the balance of the given account has changed containing the balance (in units) of the account at that date. """ real_account = realization.get_or_create( self.ledger.root_account, account_name ) postings = realization.get_postings(real_account) journal = realization.iterate_with_balance(postings) # When the balance for a commodity just went to zero, it will be # missing from the 'balance' so keep track of currencies that last had # a balance. last_currencies = None for entry, _, change, balance in journal: if change.is_empty(): continue balance = inv_to_dict(cost_or_value(balance, entry.date)) currencies = set(balance.keys()) if last_currencies: for currency in last_currencies - currencies: balance[currency] = 0 last_currencies = currencies yield {"date": entry.date, "balance": balance} @listify def net_worth(self, interval): """Compute net worth. Args: interval: A string for the interval. Returns: A list of dicts for all ends of the given interval containing the net worth (Assets + Liabilities) separately converted to all operating currencies. """ transactions = ( entry for entry in self.ledger.entries if ( isinstance(entry, Transaction) and entry.flag != flags.FLAG_UNREALIZED ) ) types = ( self.ledger.options["name_assets"], self.ledger.options["name_liabilities"], ) txn = next(transactions, None) inventory = CounterInventory() for end_date_exclusive in self.ledger.interval_ends(interval): end_date_inclusive = end_date_exclusive - datetime.timedelta( days=1 ) while txn and txn.date < end_date_exclusive: for posting in filter( lambda p: p.account.startswith(types), txn.postings ): inventory.add_position(posting) txn = next(transactions, None) yield { "date": end_date_exclusive, "balance": cost_or_value(inventory, end_date_inclusive), } @staticmethod def can_plot_query(types): """Whether we can plot the given query. Args: types: The list of types returned by the BQL query. """ return ( len(types) == 2 and types[0][1] in {str, datetime.date} and types[1][1] is Inventory ) def query(self, types, rows): """Chart for a query. Args: types: The list of result row types. rows: The result rows. """ if not self.can_plot_query(types): raise FavaAPIException("Can not plot the given chart.") if types[0][1] is datetime.date: return [ {"date": date, "balance": inv_to_dict(units(inv))} for date, inv in rows ] return [ {"group": group, "balance": inv_to_dict(units(inv))} for group, inv in rows ] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/core/extensions.py0000644000175000001440000000543600000000000017250 0ustar00jakobusers00000000000000"""Fava extensions""" import os import inspect from beancount.core.data import Custom from fava.core.helpers import FavaModule from fava.ext import find_extensions class ExtensionModule(FavaModule): """Fava extensions.""" def __init__(self, ledger): super().__init__(ledger) self._instances = {} self.reports = [] def load_file(self): all_extensions = [] custom_entries = self.ledger.all_entries_by_type[Custom] _extension_entries = extension_entries(custom_entries) for extension in _extension_entries: extensions, errors = find_extensions( os.path.dirname(self.ledger.beancount_file_path), extension ) all_extensions.extend(extensions) self.ledger.errors.extend(errors) for cls in all_extensions: module = cls.__module__ ext_config = ( _extension_entries[module] if (module in _extension_entries) else None ) if cls not in self._instances: self._instances[cls] = cls(self.ledger, ext_config) self.reports = [] for ext_class in self._instances: ext = self._instances[ext_class] if hasattr(ext, "report_title"): self.reports.append((ext.name, ext.report_title)) def run_hook(self, event, *args): """Run a hook for all extensions.""" for ext in self._instances.values(): ext.run_hook(event, *args) def template_and_extension(self, name): """Provide data to render an extension report. Args: name: The extension class qualname. Returns: Tuple of associated template source, extension instance """ for ext_class in self._instances: if ext_class.__qualname__ == name: extension_dir = os.path.dirname(inspect.getfile(ext_class)) template_path = os.path.join( extension_dir, "templates", "{}.html".format(ext_class.__qualname__), ) with open(template_path) as ext_template: return ext_template.read(), self._instances[ext_class] raise LookupError("Extension report not found.") def extension_entries(custom_entries): """Parse custom entries for extensions. They have the following format: 2016-04-01 custom "fava-extension" "my_extension" "{'my_option': {}}" """ _extension_entries = [ entry for entry in custom_entries if entry.type == "fava-extension" ] return { entry.values[0].value: ( entry.values[1].value if (len(entry.values) == 2) else None ) for entry in _extension_entries } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/core/fava_options.py0000644000175000001440000001023300000000000017530 0ustar00jakobusers00000000000000"""Fava's options. Options for Fava can be specified through Custom entries in the Beancount file. This module contains a list of possible options, the defaults and the code for parsing the options. """ import copy from collections import namedtuple import re OptionError = namedtuple("OptionError", "source message entry") InsertEntryOption = namedtuple("InsertEntryOption", "date re filename lineno") DEFAULTS = { "account-journal-include-children": True, "currency-column": 61, "collapse-pattern": [], "auto-reload": False, "conversion": "at_cost", "default-file": None, "fiscal-year-end": "12-31", "import-config": None, "import-dirs": [], "insert-entry": [], "interval": "month", "journal-show": [ "transaction", "balance", "note", "document", "custom", "budget", "query", ], "journal-show-document": ["discovered", "statement"], "journal-show-transaction": ["cleared", "pending"], "language": None, "locale": None, "show-accounts-with-zero-balance": True, "show-accounts-with-zero-transactions": True, "show-closed-accounts": False, "sidebar-show-queries": 5, "unrealized": "Unrealized", "upcoming-events": 7, "uptodate-indicator-grey-lookback-days": 60, "use-external-editor": False, } BOOL_OPTS = [ "account-journal-include-children", "auto-reload", "show-accounts-with-zero-balance", "show-accounts-with-zero-transactions", "show-closed-accounts", "use-external-editor", ] INT_OPTS = [ "currency-column", "sidebar-show-queries", "upcoming-events", "uptodate-indicator-grey-lookback-days", ] LIST_OPTS = [ "import-dirs", "journal-show", "journal-show-document", "journal-show-transaction", ] STR_OPTS = [ "collapse-pattern", "conversion", "fiscal-year-end", "import-config", "interval", "language", "locale", "unrealized", ] # options that can be specified multiple times MULTI_OPTS = ["collapse-pattern"] # pylint: disable=too-many-branches def parse_options(custom_entries): """Parse custom entries for Fava options. The format for option entries is the following: 2016-04-01 custom "fava-option" "[name]" "[value]" Args: custom_entries: A list of Custom entries. Returns: A tuple (options, errors) where options is a dictionary of all options to values, and errors contains possible parsing errors. """ options = copy.deepcopy(DEFAULTS) errors = [] for entry in custom_entries: if entry.type == "fava-option": try: key = entry.values[0].value assert key in DEFAULTS.keys() if key == "default-file": options[key] = entry.meta["filename"] elif key == "insert-entry": opt = InsertEntryOption( entry.date, re.compile(entry.values[1].value), entry.meta["filename"], entry.meta["lineno"], ) options[key].append(opt) else: value = entry.values[1].value assert isinstance(value, str) processed_value = None if key in STR_OPTS: processed_value = value elif key in BOOL_OPTS: processed_value = value.lower() == "true" elif key in INT_OPTS: processed_value = int(value) elif key in LIST_OPTS: processed_value = str(value).strip().split(" ") if processed_value is not None: if key in MULTI_OPTS: options[key].append(processed_value) else: options[key] = processed_value except (IndexError, TypeError, AssertionError): errors.append( OptionError( entry.meta, "Failed to parse fava-option entry", entry ) ) return options, errors ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/core/file.py0000644000175000001440000002460700000000000015771 0ustar00jakobusers00000000000000"""Reading/writing Beancount files.""" import codecs from hashlib import sha256 import os import re from beancount.core import data, flags from beancount.parser.printer import format_entry from fava.core.helpers import FavaAPIException, FavaModule from fava.core.misc import align class FileModule(FavaModule): """Functions related to reading/writing to Beancount files.""" def list_sources(self): """List source files. Returns: A list of all sources files, with the main file listed first. """ main_file = self.ledger.beancount_file_path return [main_file] + sorted( filter( lambda x: x != main_file, [ os.path.join(os.path.dirname(main_file), filename) for filename in self.ledger.options["include"] ], ) ) def get_source(self, path): """Get source files. Args: path: The path of the file. Returns: A string with the file contents and the `sha256sum` of the file. Raises: FavaAPIException: If the file at `path` is not one of the source files. """ if path not in self.list_sources(): raise FavaAPIException("Trying to read a non-source file") with open(path, mode="rb") as file: contents = file.read() sha256sum = sha256(contents).hexdigest() source = codecs.decode(contents) return source, sha256sum def set_source(self, path, source, sha256sum): """Write to source file. Args: path: The path of the file. source: A string with the file contents. sha256sum: Hash of the file. Returns: The `sha256sum` of the updated file. Raises: FavaAPIException: If the file at `path` is not one of the source files or if the file was changed externally. """ _, original_sha256sum = self.get_source(path) if original_sha256sum != sha256sum: raise FavaAPIException("The file changed externally.") contents = codecs.encode(source) with open(path, "w+b") as file: file.write(contents) self.ledger.extensions.run_hook("after_write_source", path, source) self.ledger.load_file() return sha256(contents).hexdigest() def insert_metadata(self, entry_hash, basekey, value): """Insert metadata into a file at lineno. Also, prevent duplicate keys. """ self.ledger.changed() entry = self.ledger.get_entry(entry_hash) key = next_key(basekey, entry.meta) insert_metadata_in_file( entry.meta["filename"], entry.meta["lineno"] - 1, key, value ) self.ledger.extensions.run_hook( "after_insert_metadata", entry, key, value ) def insert_entries(self, entries): """Insert entries. Args: entries: A list of entries. """ self.ledger.changed() for entry in sorted(entries, key=incomplete_sortkey): insert_entry(entry, self.list_sources(), self.ledger.fava_options) self.ledger.extensions.run_hook("after_insert_entry", entry) def render_entries(self, entries): """Return entries in Beancount format. Only renders :class:`.Balance` and :class:`.Transaction`. Args: entries: A list of entries. Yields: The entries rendered in Beancount format. """ excl_flags = [ flags.FLAG_PADDING, # P flags.FLAG_SUMMARIZE, # S flags.FLAG_TRANSFER, # T flags.FLAG_CONVERSIONS, # C flags.FLAG_UNREALIZED, # U flags.FLAG_RETURNS, # R flags.FLAG_MERGING, # M ] for entry in entries: if isinstance(entry, (data.Balance, data.Transaction)): if ( isinstance(entry, data.Transaction) and entry.flag in excl_flags ): continue try: yield get_entry_slice(entry)[0] + "\n" except FileNotFoundError: yield _format_entry(entry, self.ledger.fava_options) def incomplete_sortkey(entry): """Sortkey for entries that might have incomplete metadata.""" return (entry.date, data.SORT_ORDER.get(type(entry), 0)) def next_key(basekey, keys): """Returns the next unused key for basekey in the supplied array. The first try is `basekey`, followed by `basekey-2`, `basekey-3`, etc until a free one is found. """ if basekey not in keys: return basekey i = 2 while "{}-{}".format(basekey, i) in keys: i = i + 1 return "{}-{}".format(basekey, i) def leading_space(line): """Returns a string representing the leading whitespace for the specified string.""" return line[: len(line) - len(line.lstrip())] def insert_metadata_in_file(filename, lineno, key, value): """Inserts the specified metadata in the file below lineno, taking into account the whitespace in front of the line that lineno.""" with open(filename, "r", encoding="utf-8") as file: contents = file.readlines() # use the whitespace of the following line, else use double the whitespace indention = leading_space(contents[lineno + 1]) contents.insert(lineno + 1, '{}{}: "{}"\n'.format(indention, key, value)) with open(filename, "w", encoding="utf-8") as file: contents = "".join(contents) file.write(contents) def find_entry_lines(lines, lineno): """Lines of entry starting at lineno.""" entry_lines = [lines[lineno]] while True: lineno += 1 try: line = lines[lineno] except IndexError: break if not line.strip() or re.match("[0-9a-z]", line[0]): break entry_lines.append(line) return entry_lines def get_entry_slice(entry): """Get slice of the source file for an entry. Args: entry: An entry. Returns: A string containing the lines of the entry and the `sha256sum` of these lines. Raises: FavaAPIException: If the file at `path` is not one of the source files. """ with open(entry.meta["filename"], mode="r", encoding="utf-8") as file: lines = file.readlines() entry_lines = find_entry_lines(lines, entry.meta["lineno"] - 1) entry_source = "".join(entry_lines).rstrip("\n") sha256sum = sha256(codecs.encode(entry_source)).hexdigest() return entry_source, sha256sum def save_entry_slice(entry, source_slice, sha256sum): """Save slice of the source file for an entry. Args: entry: An entry. source_slice: The lines that the entry should be replaced with. sha256sum: The sha256sum of the current lines of the entry. Returns: The `sha256sum` of the new lines of the entry. Raises: FavaAPIException: If the file at `path` is not one of the source files. """ with open(entry.meta["filename"], "r", encoding="utf-8") as file: lines = file.readlines() first_entry_line = entry.meta["lineno"] - 1 entry_lines = find_entry_lines(lines, first_entry_line) entry_source = "".join(entry_lines).rstrip("\n") original_sha256sum = sha256(codecs.encode(entry_source)).hexdigest() if original_sha256sum != sha256sum: raise FavaAPIException("The file changed externally.") lines = ( lines[:first_entry_line] + [source_slice + "\n"] + lines[first_entry_line + len(entry_lines) :] ) with open(entry.meta["filename"], "w", encoding="utf-8") as file: file.writelines(lines) return sha256(codecs.encode(source_slice)).hexdigest() def insert_entry(entry, filenames, fava_options): """Insert an entry. Args: entry: An entry. filenames: List of filenames. fava_options: The ledgers fava_options. Note that the line numbers of the insert options might be updated. """ insert_options = fava_options.get("insert-entry", []) if isinstance(entry, data.Transaction): accounts = reversed([p.account for p in entry.postings]) else: accounts = [entry.account] filename, lineno = find_insert_position( accounts, entry.date, insert_options, filenames ) content = _format_entry(entry, fava_options) + "\n" with open(filename, "r", encoding="utf-8") as file: contents = file.readlines() contents.insert(lineno, content) with open(filename, "w", encoding="utf-8") as file: file.writelines(contents) for index, option in enumerate(insert_options): added_lines = content.count("\n") + 1 if option.filename == filename and option.lineno > lineno: insert_options[index] = option._replace( lineno=lineno + added_lines ) def _format_entry(entry, fava_options): """Wrapper that strips unnecessary whitespace from format_entry.""" meta = { key: entry.meta[key] for key in entry.meta if not key.startswith("_") } entry = entry._replace(meta=meta) string = align(format_entry(entry), fava_options) string = string.replace("", "") return "\n".join((line.rstrip() for line in string.split("\n"))) def find_insert_position(accounts, date, insert_options, filenames): """Find insert position for an account. Args: accounts: A list of accounts. date: A date. Only InsertOptions before this date will be considered. insert_options: A list of InsertOption. filenames: List of Beancount files. Returns: A tuple of the filename and the line number. """ # Make no assumptions about the order of insert_options entries and instead # sort them ourselves (by descending dates) sorted_insert_options = sorted( insert_options, key=lambda x: x.date, reverse=True ) for account in accounts: for insert_option in sorted_insert_options: if insert_option.date >= date: continue if insert_option.re.match(account): return (insert_option.filename, insert_option.lineno - 1) return ( filenames[0], len(open(filenames[0], encoding="utf-8").readlines()) + 1, ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/core/filters.py0000644000175000001440000002405000000000000016512 0ustar00jakobusers00000000000000"""Entry filters.""" import re import ply.yacc from beancount.core import account from beancount.core.data import Custom, Transaction from beancount.ops import summarize from fava.util.date import parse_date from fava.core.helpers import FilterException class Token: """A token having a certain type and value. The lexer attribute only exists since PLY writes to it in case of a parser error. """ __slots__ = ["type", "value", "lexer"] def __init__(self, type_, value): self.type = type_ self.value = value def __repr__(self): return "Token({}, {})".format(self.type, self.value) class FilterSyntaxLexer: """Lexer for Fava's filter syntax.""" # pylint: disable=missing-docstring,invalid-name,no-self-use tokens = ("ANY", "ALL", "KEY", "LINK", "STRING", "TAG") RULES = ( ("LINK", r"\^[A-Za-z0-9\-_/.]+"), ("TAG", r"\#[A-Za-z0-9\-_/.]+"), ("KEY", r"[a-z][a-zA-Z0-9\-_]+:"), ("ALL", r"all\("), ("ANY", r"any\("), ("STRING", r'\w[-\w]*|"[^"]*"|\'[^\']*\''), ) regex = re.compile( "|".join(("(?P<{}>{})".format(name, rule) for name, rule in RULES)) ) def LINK(self, token, value): return token, value[1:] def TAG(self, token, value): return token, value[1:] def KEY(self, token, value): return token, value[:-1] def ALL(self, token, _): return token, token def ANY(self, token, _): return token, token def STRING(self, token, value): if value[0] in ['"', "'"]: return token, value[1:-1] return token, value def lex(self, data): """A generator yielding all tokens in a given line. Arguments: data: A string, the line to lex. Yields: All Tokens in the line. """ ignore = " \t" literals = "-,()" regex = self.regex.match pos = 0 length = len(data) while pos < length: char = data[pos] if char in ignore: pos += 1 continue match = regex(data, pos) if match: value = match.group() pos += len(value) token = match.lastgroup func = getattr(self, token) ret = func(token, value) if ret: yield Token(*ret) elif char in literals: yield Token(char, char) pos += 1 else: raise FilterException( "filter", 'Illegal character "{}" in filter: '.format(char) ) class Match: """Match a string.""" __slots__ = ["match"] def __init__(self, search): try: self.match = re.compile(search, re.IGNORECASE).match except re.error: self.match = lambda string: string == search def __call__(self, string): return self.match(string) class FilterSyntaxParser: # pylint: disable=missing-docstring,invalid-name,no-self-use precedence = (("left", "AND"), ("right", "UMINUS")) tokens = FilterSyntaxLexer.tokens def p_error(self, _): raise FilterException("filter", "Failed to parse filter: ") def p_filter(self, p): """ filter : expr """ p[0] = p[1] def p_expr(self, p): """ expr : simple_expr """ p[0] = p[1] def p_expr_all(self, p): """ expr : ALL expr ')' """ expr = p[2] def _match_postings(entry): return all( expr(posting) for posting in getattr(entry, "postings", []) ) p[0] = _match_postings def p_expr_any(self, p): """ expr : ANY expr ')' """ expr = p[2] def _match_postings(entry): return any( expr(posting) for posting in getattr(entry, "postings", []) ) p[0] = _match_postings def p_expr_parentheses(self, p): """ expr : '(' expr ')' """ p[0] = p[2] def p_expr_and(self, p): """ expr : expr expr %prec AND """ left, right = p[1], p[2] def _and(entry): return left(entry) and right(entry) p[0] = _and def p_expr_or(self, p): """ expr : expr ',' expr """ left, right = p[1], p[3] def _or(entry): return left(entry) or right(entry) p[0] = _or def p_expr_negated(self, p): """ expr : '-' expr %prec UMINUS """ func = p[2] def _neg(entry): return not func(entry) p[0] = _neg def p_simple_expr_TAG(self, p): """ simple_expr : TAG """ tag = p[1] def _tag(entry): return hasattr(entry, "tags") and (tag in entry.tags) p[0] = _tag def p_simple_expr_LINK(self, p): """ simple_expr : LINK """ link = p[1] def _link(entry): return hasattr(entry, "links") and (link in entry.links) p[0] = _link def p_simple_expr_STRING(self, p): """ simple_expr : STRING """ string = p[1] match = Match(string) def _string(entry): for name in ("narration", "payee", "comment"): value = getattr(entry, name, "") if value and match(value): return True return False p[0] = _string def p_simple_expr_key(self, p): """ simple_expr : KEY STRING """ key, value = p[1], p[2] match = Match(value) def _key(entry): if hasattr(entry, key): return match(str(getattr(entry, key) or "")) if key in entry.meta: return match(str(entry.meta.get(key))) return False p[0] = _key class EntryFilter: """Filters a list of entries. """ def __init__(self, options, fava_options): self.options = options self.fava_options = fava_options self.value = None def set(self, value): """Set the filter. Subclasses should check for validity of the value in this method. """ if value == self.value: return False self.value = value return True def _include_entry(self, entry): raise NotImplementedError def _filter(self, entries): return [entry for entry in entries if self._include_entry(entry)] def apply(self, entries): """Apply filter. Args: entries: a list of entries. options: an options_map. Returns: A list of filtered entries. """ if self.value: return self._filter(entries) return entries def __bool__(self): return bool(self.value) class TimeFilter(EntryFilter): # pylint: disable=abstract-method """Filter by dates.""" def __init__(self, *args): super().__init__(*args) self.begin_date = None self.end_date = None def set(self, value): if value == self.value: return False self.value = value if not self.value: return True self.begin_date, self.end_date = parse_date( self.value, self.fava_options["fiscal-year-end"] ) if not self.begin_date: self.value = None raise FilterException( "time", "Failed to parse date: {}".format(value) ) return True def _filter(self, entries): entries, _ = summarize.clamp_opt( entries, self.begin_date, self.end_date, self.options ) return entries LEXER = FilterSyntaxLexer() PARSE = ply.yacc.yacc( errorlog=ply.yacc.NullLogger(), write_tables=False, debug=False, module=FilterSyntaxParser(), ).parse class AdvancedFilter(EntryFilter): """Filter by tags and links and keys.""" def __init__(self, *args): super().__init__(*args) self._include = None def set(self, value): if value == self.value: return False self.value = value if value and value.strip(): try: tokens = LEXER.lex(value.strip()) self._include = PARSE( lexer="NONE", tokenfunc=lambda toks=tokens: next(toks, None), ) except FilterException as exception: exception.message = exception.message + value self.value = None raise exception else: self._include = None return True def _include_entry(self, entry): if self._include: return self._include(entry) return True def entry_account_predicate(entry, predicate): """Predicate for filtering by account. Args: entry: An entry. predicate: A predicate for account names. Returns: True if predicate is True for any account of the entry. """ if isinstance(entry, Transaction): return any(predicate(posting.account) for posting in entry.postings) if isinstance(entry, Custom): return any( predicate(val.value) for val in entry.values if val.dtype == account.TYPE ) return hasattr(entry, "account") and predicate(entry.account) class AccountFilter(EntryFilter): """Filter by account. The filter string can either a regular expression or a parent account. """ def __init__(self, *args): super().__init__(*args) self.match = None def set(self, value): if value == self.value: return False self.value = value self.match = Match(value or "") return True def _account_predicate(self, name): return account.has_component(name, self.value) or self.match(name) def _include_entry(self, entry): return entry_account_predicate(entry, self._account_predicate) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/core/helpers.py0000644000175000001440000000132600000000000016505 0ustar00jakobusers00000000000000"""Exceptions and module base class.""" class FavaAPIException(Exception): """Fava's base exception class.""" def __init__(self, message): super().__init__() self.message = message def __str__(self): return self.message class FilterException(FavaAPIException): """Filter exception.""" def __init__(self, filter_type, message): super().__init__(message) self.filter_type = filter_type def __str__(self): return self.message class FavaModule: """Base class for the "modules" of FavaLedger.""" def __init__(self, ledger): self.ledger = ledger def load_file(self): """Gets called when the file has been (re)loaded.""" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/core/ingest.py0000644000175000001440000001061500000000000016335 0ustar00jakobusers00000000000000"""Ingest helper functions.""" from collections import namedtuple import datetime from os import path import os import runpy import sys import traceback from beancount.ingest import cache from beancount.ingest import extract from beancount.ingest import identify from fava.core.helpers import FavaModule IngestError = namedtuple("IngestError", "source message entry") FileImporters = namedtuple("FileImporters", "name basename importers") FileImportInfo = namedtuple( "FileImportInfo", "importer_name account date name" ) def file_import_info(filename: str, importer) -> FileImportInfo: """Generate info about a file with an importer.""" # pylint: disable=broad-except file = cache.get_file(filename) try: account = importer.file_account(file) except Exception: account = "" try: date = importer.file_date(file) except Exception: date = datetime.date.today() try: name = importer.file_name(file) except Exception: name = path.basename(filename) return FileImportInfo(importer.name(), account, date, name) class IngestModule(FavaModule): """Exposes ingest functionality.""" def __init__(self, ledger): super().__init__(ledger) self.config = [] self.importers = {} self.mtime = None @property def module_path(self): """The path to the importer configuration.""" config_path = self.ledger.fava_options["import-config"] if not config_path: return None return self.ledger.join_path(config_path) def load_file(self): if not self.ledger.fava_options["import-config"]: return if not path.exists(self.module_path) or path.isdir(self.module_path): self.ledger.errors.append( IngestError( None, "File does not exist: '{}'".format(self.module_path), None, ) ) return if os.stat(self.module_path).st_mtime_ns == self.mtime: return try: mod = runpy.run_path(self.module_path) except Exception: # pylint: disable=broad-except message = "".join(traceback.format_exception(*sys.exc_info())) self.ledger.errors.append( IngestError( None, "Error in importer '{}': {}".format( str(self.module_path), message ), None, ) ) return self.mtime = os.stat(self.module_path).st_mtime_ns self.config = mod["CONFIG"] self.importers = { importer.name(): importer for importer in self.config } def import_data(self): """Identify files and importers that can be imported. Returns: A dict mapping directories to lists of :class:`.FileImportInfo`. """ if not self.config: return {} ret = {} for directory in self.ledger.fava_options["import-dirs"]: full_path = self.ledger.join_path(directory) files = list(identify.find_imports(self.config, full_path)) ret[directory] = [] for (filename, importers) in files: basename = path.basename(filename) infos = [ file_import_info(filename, importer) for importer in importers ] ret[directory].append(FileImporters(filename, basename, infos)) return ret def extract(self, filename, importer_name): """Extract entries from filename with the specified importer. Args: filename: The full path to a file. importer_name: The name of an importer that matched the file. Returns: A list of new imported entries. """ if not filename or not importer_name or not self.config: return [] if os.stat(self.module_path).st_mtime_ns > self.mtime: self.load_file() new_entries = extract.extract_from_file( filename, self.importers.get(importer_name), existing_entries=self.ledger.all_entries, ) new_entries = extract.find_duplicate_entries( [(filename, new_entries)], self.ledger.all_entries )[0][1] return new_entries ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/core/inventory.py0000644000175000001440000000433700000000000017105 0ustar00jakobusers00000000000000"""Alternative implementation of Beancount's Inventory.""" from beancount.core.amount import Amount from beancount.core.number import ZERO from beancount.core.position import Position # pylint: disable=abstract-method class CounterInventory(dict): """A lightweight inventory. This is intended as a faster alternative to Beancount's Inventory class. Due to not using a list, for inventories with a lot of different positions, inserting is much faster. The keys should be tuples ``(currency, cost)``. """ def is_empty(self): """Check if the inventory is empty.""" return not bool(self) def add(self, key, number): """Add a number to key.""" new_num = number + self.get(key, ZERO) if new_num == ZERO: self.pop(key, None) else: self[key] = new_num def reduce(self, reducer, *args): """Reduce inventory. Note that this returns a simple :class:`CounterInventory` with just currencies as keys. """ counter = CounterInventory() for (currency, cost), number in self.items(): pos = Position(Amount(number, currency), cost) amount = reducer(pos, *args) counter.add(amount.currency, amount.number) return counter def add_amount(self, amount, cost=None): """Add an Amount to the inventory.""" key = (amount.currency, cost) self.add(key, amount.number) def add_position(self, pos): """Add a Position or Posting to the inventory.""" self.add_amount(pos.units, pos.cost) def __neg__(self): return CounterInventory({key: -num for key, num in self.items()}) def __add__(self, other): counter = CounterInventory(self) counter.add_inventory(other) return counter def add_inventory(self, counter): """Add another :class:`CounterInventory`.""" if not self: self.update(counter) else: self_get = self.get for key, num in counter.items(): new_num = num + self_get(key, ZERO) if new_num == ZERO: self.pop(key, None) else: self[key] = new_num ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/core/misc.py0000644000175000001440000000553300000000000016002 0ustar00jakobusers00000000000000"""Some miscellaneous reports.""" from collections import namedtuple import datetime import io import re from beancount.core.data import Custom, Event from beancount.core import amount from fava.core.helpers import FavaModule from fava.core.fava_options import DEFAULTS FavaError = namedtuple("FavaError", "source message entry") class FavaMisc(FavaModule): """Provides access to some miscellaneous reports.""" def __init__(self, ledger): super().__init__(ledger) self.sidebar_links = None self.upcoming_events = None def load_file(self): custom_entries = self.ledger.all_entries_by_type[Custom] self.sidebar_links = sidebar_links(custom_entries) self.upcoming_events = upcoming_events( self.ledger.all_entries_by_type[Event], self.ledger.fava_options["upcoming-events"], ) if not self.ledger.options["operating_currency"]: self.ledger.errors.append( FavaError( None, "No operating currency specified. " "Please add one to your beancount file.", None, ) ) def sidebar_links(custom_entries): """Parse custom entries for links. They have the following format: 2016-04-01 custom "fava-sidebar-link" "2014" "/income_statement/?time=2014" """ sidebar_link_entries = [ entry for entry in custom_entries if entry.type == "fava-sidebar-link" ] return [ (entry.values[0].value, entry.values[1].value) for entry in sidebar_link_entries ] def upcoming_events(events, max_delta): """Parse entries for upcoming events. Args: events: A list of events. max_delta: Number of days that should be considered. Returns: A list of the Events in entries that are less than `max_delta` days away. """ today = datetime.date.today() upcoming = [] for event in events: delta = event.date - today if delta.days >= 0 and delta.days < max_delta: upcoming.append(event) return upcoming def align(string, fava_options): """Align currencies in one column.""" column = fava_options.get("currency-column", DEFAULTS["currency-column"]) output = io.StringIO() for line in string.splitlines(): match = re.match( r'([^";]*?)\s+([-+]?\s*[\d,]+(?:\.\d*)?)\s+({}\b.*)'.format( amount.CURRENCY_RE ), line, ) if match: prefix, number, rest = match.groups() num_of_spaces = column - len(prefix) - len(number) - 4 spaces = " " * num_of_spaces output.write(prefix + spaces + " " + number + " " + rest) else: output.write(line) output.write("\n") return output.getvalue() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/core/number.py0000644000175000001440000000461400000000000016336 0ustar00jakobusers00000000000000"""Formatting numbers.""" import copy from babel.core import Locale, UnknownLocaleError from beancount.core.display_context import Precision from fava.core.helpers import FavaModule from fava.core.fava_options import OptionError class DecimalFormatModule(FavaModule): """Formatting numbers.""" def __init__(self, ledger): super().__init__(ledger) self.locale = None self.patterns = {} self.default_pattern = None def load_file(self): self.locale = None locale_option = self.ledger.fava_options["locale"] if self.ledger.options["render_commas"] and not locale_option: locale_option = "en" self.ledger.fava_options["locale"] = locale_option if locale_option: try: self.locale = Locale.parse(locale_option) except UnknownLocaleError: self.locale = None self.ledger.errors.append( OptionError( None, "Unknown locale: {}.".format( self.ledger.fava_options["locale"] ), None, ) ) if self.locale: self.default_pattern = copy.copy( self.locale.decimal_formats.get(None) ) self.default_pattern.frac_prec = (2, 2) else: self.default_pattern = "{:.2f}" dcontext = self.ledger.options["dcontext"] for currency, ccontext in dcontext.ccontexts.items(): precision = ccontext.get_fractional(Precision.MOST_COMMON) if self.locale: pattern = copy.copy(self.locale.decimal_formats.get(None)) pattern.frac_prec = (precision, precision) else: pattern = "{:." + str(precision) + "f}" self.patterns[currency] = pattern def __call__(self, value, currency=None): """Format a decimal to the right number of decimal digits with locale. Arguments: value: A decimal number. currency: A currency string or None. Returns: A string, the formatted decimal. """ pattern = self.patterns.get(currency, self.default_pattern) if not self.locale: return pattern.format(value) return pattern.apply(value, self.locale) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/core/query_shell.py0000644000175000001440000001400700000000000017377 0ustar00jakobusers00000000000000"""For using the Beancount shell from Fava.""" import contextlib import io import readline import textwrap from beancount.core.data import Query from beancount.query import query_compile, query_execute, query_parser, shell from beancount.query.query import run_query from beancount.utils import pager from fava.core.helpers import FavaAPIException, FavaModule from fava.util.excel import to_csv, to_excel, HAVE_EXCEL # This is to limit the size of the history file. Fava is not using readline at # all, but Beancount somehow still is... readline.set_history_length(1000) class QueryShell(shell.BQLShell, FavaModule): """A light wrapper around Beancount's shell.""" # pylint: disable=too-many-instance-attributes def __init__(self, ledger): self.ledger = ledger self.buffer = io.StringIO() self.result = None super().__init__(True, None, self.buffer) self.stdout = self.buffer self.entries = None self.errors = None self.options_map = None self.queries = [] def load_file(self): self.queries = self.ledger.all_entries_by_type[Query] def add_help(self): "Attach help functions for each of the parsed token handlers." for attrname, func in list(shell.BQLShell.__dict__.items()): if attrname[:3] != "on_": continue command_name = attrname[3:] setattr( self.__class__, "help_{}".format(command_name.lower()), lambda _, fun=func: print( textwrap.dedent(fun.__doc__).strip(), file=self.outfile ), ) def _loadfun(self): self.entries = self.ledger.entries self.errors = self.ledger.errors self.options_map = self.ledger.options def get_pager(self): """No real pager, just a wrapper that doesn't close self.buffer.""" return pager.flush_only(self.buffer) def noop(self, _): """Doesn't do anything in Fava's query shell.""" print(self.noop.__doc__, file=self.outfile) on_Reload = noop do_exit = noop do_quit = noop do_EOF = noop def on_Select(self, statement): # pylint: disable=invalid-name try: c_query = query_compile.compile( statement, self.env_targets, self.env_postings, self.env_entries, ) except query_compile.CompilationError as exc: print("ERROR: {}.".format(str(exc).rstrip(".")), file=self.outfile) return rtypes, rrows = query_execute.execute_query( c_query, self.entries, self.options_map ) if not rrows: print("(empty)", file=self.outfile) self.result = rtypes, rrows def execute_query(self, query): """Run a query. Arguments: query: A query string. Returns: A tuple (contents, types, rows) where either the first or the last two entries are None. If the query result is a table, it will be contained in ``types`` and ``rows``, otherwise the result will be contained in ``contents`` (as a string). """ self._loadfun() with contextlib.redirect_stdout(self.buffer): self.onecmd(query) contents = self.buffer.getvalue() self.buffer.truncate(0) if self.result is None: return (contents, None, None) types, rows = self.result self.result = None return (None, types, rows) def on_RunCustom(self, run_stmt): """Run a custom query.""" name = run_stmt.query_name if name is None: # List the available queries. for query in self.queries: print(query.name) else: try: query = next( (query for query in self.queries if query.name == name) ) except StopIteration: print("ERROR: Query '{}' not found".format(name)) else: statement = self.parser.parse(query.query_string) self.dispatch(statement) def query_to_file(self, query_string, result_format): """Get query result as file. Arguments: query_string: A string, the query to run. result_format: The file format to save to. Returns: A tuple (name, data), where name is either 'query_result' or the name of a custom query if the query string is 'run name_of_query'. ``data`` contains the file contents. Raises: FavaAPIException: If the result format is not supported or the query failed. """ name = "query_result" try: statement = self.parser.parse(query_string) except query_parser.ParseError as exception: raise FavaAPIException(str(exception)) if statement.__class__.__name__ == "RunCustom": name = statement.query_name try: query = next( (query for query in self.queries if query.name == name) ) except StopIteration: raise FavaAPIException('Query "{}" not found.'.format(name)) query_string = query.query_string try: types, rows = run_query( self.ledger.all_entries, self.ledger.options, query_string, numberify=True, ) except ( query_compile.CompilationError, query_parser.ParseError, ) as exception: raise FavaAPIException(str(exception)) if result_format == "csv": data = to_csv(types, rows) else: if not HAVE_EXCEL: raise FavaAPIException("Result format not supported.") data = to_excel(types, rows, result_format, query_string) return name, data QueryShell.on_Select.__doc__ = shell.BQLShell.on_Select.__doc__ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/core/tree.py0000644000175000001440000001236100000000000016003 0ustar00jakobusers00000000000000"""Account balance trees.""" import collections from beancount.core import account, convert from beancount.core.data import Open from fava.core.inventory import CounterInventory from fava.template_filters import cost_or_value class TreeNode: """A node in the account tree.""" __slots__ = ("name", "children", "balance", "balance_children", "has_txns") def __init__(self, name): #: str: Account name. self.name = name #: A list of :class:`.TreeNode`, its children. self.children = [] #: :class:`.CounterInventory`: The cumulative account balance. self.balance_children = CounterInventory() #: :class:`.CounterInventory`: The account balance. self.balance = CounterInventory() #: bool: True if the account has any transactions. self.has_txns = False def serialise(self, end): """Serialise the account. Args: end: A date to use for cost conversions. """ children = [child.serialise(end) for child in self.children] return { "account": self.name, "balance_children": cost_or_value(self.balance_children, end), "balance": cost_or_value(self.balance, end), "children": children, } class Tree(dict): """Account tree. Args: entries: A list of entries to compute balances from. """ def __init__(self, entries=None): dict.__init__(self) self.get("", insert=True) if entries: account_balances = collections.defaultdict(CounterInventory) for entry in entries: if isinstance(entry, Open): self.get(entry.account, insert=True) for posting in getattr(entry, "postings", []): account_balances[posting.account].add_position(posting) for name, balance in sorted(account_balances.items()): self.insert(name, balance) def ancestors(self, name): """Ancestors of an account. Args: name: An account name. Yields: The ancestors of the given account from the bottom up. """ while name: name = account.parent(name) yield self.get(name) def insert(self, name, balance): """Insert account with a balance. Insert account and update its balance and the balances of its ancestors. Args: name: An account name. balance: The balance of the account. """ node = self.get(name, insert=True) node.balance.add_inventory(balance) node.balance_children.add_inventory(balance) node.has_txns = True for parent_node in self.ancestors(name): parent_node.balance_children.add_inventory(balance) def get(self, name, insert=False): """Get an account. Args: name: An account name. insert: If True, insert the name into the tree if it does not exist. Returns: TreeNode: The account of that name or an empty account if the account is not in the tree. """ try: return self[name] except KeyError: node = TreeNode(name) if insert: if name: parent = self.get(account.parent(name), insert=True) parent.children.append(node) self[name] = node return node def net_profit(self, options, account_name): """Calculate the net profit. Args: options: The Beancount options. account_name: The name to use for the account containing the net profit. """ income = self.get(options["name_income"]) expenses = self.get(options["name_expenses"]) net_profit = Tree() net_profit.insert( account_name, income.balance_children + expenses.balance_children ) return net_profit.get(account_name) def cap(self, options, unrealized_account): """Transfer Income and Expenses, add conversions and unrealized gains. Args: options: The Beancount options. unrealized_account: The name of the account to post unrealized gains to (as a subaccount of Equity). """ equity = options["name_equity"] conversions = CounterInventory( { (currency, None): -number for currency, number in self.get("") .balance_children.reduce(convert.get_cost) .items() } ) # Add conversions self.insert( equity + ":" + options["account_current_conversions"], conversions ) # Insert unrealized gains. self.insert( equity + ":" + unrealized_account, -self.get("").balance_children ) # Transfer Income and Expenses self.insert( equity + ":" + options["account_current_earnings"], self.get(options["name_income"]).balance_children, ) self.insert( equity + ":" + options["account_current_earnings"], self.get(options["name_expenses"]).balance_children, ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/core/watcher.py0000644000175000001440000000263000000000000016477 0ustar00jakobusers00000000000000"""A simple file and folder watcher.""" import os class Watcher: """A simple file and folder watcher. For folders, only checks mtime of the folder and all subdirectories. So a file change won't be noticed, but only new/deleted files. """ __slots__ = ["_files", "_folders", "_last_checked"] def __init__(self): self._files = [] self._folders = [] self._last_checked = 0 def update(self, files, folders): """Update the folders/files to watch. Args: files: A list of file paths. folders: A list of paths to folders. """ self._files = list(files) self._folders = list(folders) self.check() def check(self): """Check for changes. Returns: `True` if there was a file change in one of the files or folders, `False` otherwise. """ latest_mtime = 0 for path in self._files: mtime = os.stat(path).st_mtime_ns if mtime > latest_mtime: latest_mtime = mtime for path in self._folders: for dirpath, _, _ in os.walk(path): mtime = os.stat(dirpath).st_mtime_ns if mtime > latest_mtime: latest_mtime = mtime changed = bool(latest_mtime != self._last_checked) self._last_checked = latest_mtime return changed ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1581856610.6126237 fava-1.14/fava/ext/0000755000175000001440000000000000000000000014337 5ustar00jakobusers00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/ext/__init__.py0000644000175000001440000000442300000000000016453 0ustar00jakobusers00000000000000"""Fava's extension system.""" from collections import namedtuple import importlib import inspect import sys import ast FavaExtensionError = namedtuple("FavaExtensionError", "source message entry") class FavaExtensionBase: """Base class for extensions for Fava. Any extension should inherit from this class. :func:`find_extension` will discover all subclasses of this class in the specified modules. """ def __init__(self, ledger, config=None): """ Base init function. Args: ledger: Input ledger file. config: Configuration options string passed from the beancount file's 'fava-extension' line. """ self.ledger = ledger try: self.config = ast.literal_eval(config) except ValueError: self.config = None self.name = self.__class__.__qualname__ def run_hook(self, event, *args): """Run a hook. Args: event: One of the possible events. """ try: getattr(self, event)(*args) except AttributeError: pass def find_extensions(base_path, name): """Find extensions in a module. Args: base_path: The module can be relative to this path. name: The name of the module containing the extensions. Returns: A tuple (classes, errors) where classes is a list of subclasses of :class:`FavaExtensionBase` found in ``name``. """ classes = [] sys.path.insert(0, base_path) try: module = importlib.import_module(name) except ImportError: return ( [], [ FavaExtensionError( None, 'Importing module "{}" failed.'.format(name), None ) ], ) for _, obj in inspect.getmembers(module, inspect.isclass): if issubclass(obj, FavaExtensionBase) and obj != FavaExtensionBase: classes.append(obj) sys.path.pop(0) if not classes: return ( [], [ FavaExtensionError( None, 'Module "{}" contains no extensions.'.format(name), None, ) ], ) return classes, [] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/ext/auto_commit.py0000644000175000001440000000166300000000000017237 0ustar00jakobusers00000000000000"""Auto-commit hook for Fava. This mainly serves as an example how Fava's extension systems, which only really does hooks at the moment, works. """ # pylint: disable=missing-docstring import os import subprocess from fava.ext import FavaExtensionBase class AutoCommit(FavaExtensionBase): # pragma: no cover def _run(self, args): cwd = os.path.dirname(self.ledger.beancount_file_path) subprocess.call(args, cwd=cwd, stdout=subprocess.DEVNULL) def after_write_source(self, path, _): message = "autocommit: file saved" self._run(["git", "add", path]) self._run(["git", "commit", "-m", message]) def after_insert_metadata(self, *_): message = "autocommit: metadata added" self._run(["git", "commit", "-am", message]) def after_insert_entry(self, entry): message = "autocommit: entry on {}".format(entry.date) self._run(["git", "commit", "-am", message]) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1581856610.6126237 fava-1.14/fava/ext/portfolio_list/0000755000175000001440000000000000000000000017407 5ustar00jakobusers00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/ext/portfolio_list/__init__.py0000644000175000001440000001103000000000000021513 0ustar00jakobusers00000000000000"""Portfolio list extension for Fava. This is a simple example of Fava's extension reports system. """ import re from beancount.core.data import iter_entry_dates, Open from beancount.core.number import ZERO, Decimal from fava.ext import FavaExtensionBase from fava.template_filters import cost_or_value from fava.core.tree import Tree from fava.core.helpers import FavaAPIException class PortfolioList(FavaExtensionBase): # pragma: no cover """Sample Extension Report that just prints out an Portfolio List. """ report_title = "Portfolio List" def portfolio_accounts(self, begin=None, end=None): """An account tree based on matching regex patterns.""" if begin: tree = Tree(iter_entry_dates(self.ledger.entries, begin, end)) else: tree = self.ledger.root_tree portfolios = [] for option in self.config: opt_key = option[0] if opt_key == "account_name_pattern": portfolio = self._account_name_pattern(tree, end, option[1]) elif opt_key == "account_open_metadata_pattern": portfolio = self._account_metadata_pattern( tree, end, option[1][0], option[1][1] ) else: raise FavaAPIException("Portfolio List: Invalid option.") portfolios.append(portfolio) return portfolios def _account_name_pattern(self, tree, date, pattern): """ Returns portfolio info based on matching account name. Args: tree: Ledger root tree node. date: Date. pattern: Account name regex pattern. Return: Data structured for use with a querytable (types, rows). """ title = "Account names matching: '" + pattern + "'" selected_accounts = [] regexer = re.compile(pattern) for acct in tree.keys(): if (regexer.match(acct) is not None) and ( acct not in selected_accounts ): selected_accounts.append(acct) selected_nodes = [tree[x] for x in selected_accounts] portfolio_data = self._portfolio_data(selected_nodes, date) return title, portfolio_data def _account_metadata_pattern(self, tree, date, metadata_key, pattern): """ Returns portfolio info based on matching account open metadata. Args: tree: Ledger root tree node. date: Date. metadata_key: Metadata key to match for in account open. pattern: Metadata value's regex pattern to match for. Return: Data structured for use with a querytable - (types, rows). """ title = ( "Accounts with '" + metadata_key + "' metadata matching: '" + pattern + "'" ) selected_accounts = [] regexer = re.compile(pattern) for entry in self.ledger.all_entries_by_type[Open]: if (metadata_key in entry.meta) and ( regexer.match(entry.meta[metadata_key]) is not None ): selected_accounts.append(entry.account) selected_nodes = [tree[x] for x in selected_accounts] portfolio_data = self._portfolio_data(selected_nodes, date) return title, portfolio_data def _portfolio_data(self, nodes, date): """ Turn a portfolio of tree nodes into querytable-style data. Args: nodes: Account tree nodes. date: Date. Return: types: Tuples of column names and types as strings. rows: Dictionaries of row data by column names. """ operating_currency = self.ledger.options["operating_currency"][0] acct_type = ("account", str(str)) bal_type = ("balance", str(Decimal)) alloc_type = ("allocation", str(Decimal)) types = [acct_type, bal_type, alloc_type] rows = [] portfolio_total = ZERO for node in nodes: row = {} row["account"] = node.name balance = cost_or_value(node.balance, date) if operating_currency in balance: balance_dec = balance[operating_currency] portfolio_total += balance_dec row["balance"] = balance_dec rows.append(row) for row in rows: if "balance" in row: row["allocation"] = round( (row["balance"] / portfolio_total) * 100, 2 ) return types, rows ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1581856610.615957 fava-1.14/fava/ext/portfolio_list/templates/0000755000175000001440000000000000000000000021405 5ustar00jakobusers00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/ext/portfolio_list/templates/PortfolioList.html0000644000175000001440000000043700000000000025110 0ustar00jakobusers00000000000000{% import "_query_table.html" as querytable with context %}

Portfolio List Sample Report Extension


{% for portfolio in extension.portfolio_accounts(None, None) %}

{{portfolio[0]}}

{{ querytable.querytable(None, *portfolio[1]) }}
{% endfor %} ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1581856610.615957 fava-1.14/fava/help/0000755000175000001440000000000000000000000014467 5ustar00jakobusers00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/help/__init__.py0000644000175000001440000000046200000000000016602 0ustar00jakobusers00000000000000""" List of all available help pages. """ HELP_PAGES = { "_index": "Index", "budgets": "Budgets", "import": "Import", "options": "Options", "beancount_syntax": "Beancount Syntax", "features": "Fava's features", "filters": "Filtering entries", "extensions": "Extensions", } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/help/_index.md0000644000175000001440000000330100000000000016254 0ustar00jakobusers00000000000000Welcome to the help pages for Fava! There are pages for the following topics: - [Beancount Syntax]({{ url_for('help_page', page_slug='beancount_syntax') }}) - short overview of the syntax. - [Budgets]({{ url_for('help_page', page_slug='budgets') }}) - how to use Fava's budgeting feature. - [Fava's Features]({{ url_for('help_page', page_slug='features') }}) - the features in detail. - [Filtering entries]({{ url_for('help_page', page_slug='filters') }}) - how to filter the entries. - [Extensions]({{ url_for('help_page', page_slug='extensions') }}) - how Fava can be extended. - [Import]({{ url_for('help_page', page_slug='import') }}) - the import system. - [Options]({{ url_for('help_page', page_slug='options') }}) - the available options. Fava comes with keyboard shortcuts - press ? on any page to see the available ones. When charts are visible, c and C will show to the next and previous chart respectively. If you started Fava from the command line, you can run `fava --help` to see all the available command line options. If you discover a bug in Fava, or have some ideas for improvement, please open a [bug report](https://github.com/beancount/fava/issues). ### Related websites - Fava's [website](https://beancount.github.io/fava/), [chat](https://gitter.im/beancount/fava) and Fava on [GitHub](https://github.com/beancount/fava), - Beancount's [documentation](http://furius.ca/beancount/doc/index), [mailing list](https://groups.google.com/forum/#!forum/beancount), and [bug tracker](https://bitbucket.org/blais/beancount/issues), - An overview of other implementations of command-line accounting: [Plain Text Accounting](http://plaintextaccounting.org). ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/help/beancount_syntax.md0000644000175000001440000001167400000000000020406 0ustar00jakobusers00000000000000Below is a short reference of the Beancount language syntax. Also see the full [Syntax Documentation](http://furius.ca/beancount/doc/syntax) and the [Syntax Cheat Sheet](http://furius.ca/beancount/doc/cheatsheet). Beancount defines a language in which financial transactions are entered into a text-file, which then can be processed by Beancount. There are a few building blocks that are important to understand Beancount's syntax: - Commodities, - Accounts, - Directives. ## Commodities All in CAPS: `USD`, `EUR`, `CAD`, `GOOG`, `AAPL`, `RBF1005`, `HOME_MAYST`, `AIRMILES`, `HOURS`. ## Accounts Account are given by a colon-separated list of capitalized words. They must begin with one of the five root accounts listed in the table below. The separation by colons defines an implicit hierarchy, for example we say that `Assets:Cash` is a sub-account of `Assets`. | Name | Type | Contains | Examples | | ------------- | ---- | ---------------------------- | ------------------------- | | `Assets` | + | Cash, Checking-Account, etc. | `Assets:Checking` | | `Liabilities` | - | Credit Card, etc. | `Liabilities:CreditCard` | | `Income` | - | Salary, etc. | `Income:EmployerA` | | `Expenses` | + | Expense categories | `Expenses:Fun:Cinema` | | `Equity` | - | Almost always auto-generated | `Equity:Opening-Balances` | The names of the five root accounts can be changed with the following options:
## Directives The basic building block are **directives** (also called **entries**). Most directives start with a date, then the type of the directive, and then directive-specific arguments. The ordering of directives in the input-file does not matter, because Beancount orders them based on the date of each directive. General syntax: `YYYY-MM-DD ` ### Open and Close accounts To open or close an account use the `open` and `close` directives:
### Commodities Declaring commodities is optional. Use this if you want to attach metadata by currency.
### Prices You can use this directive to fill the historical price database:
### Notes
### Documents
### Transactions
### Postings
### Balance Assertions and Padding Asserts the amount for only the given currency:
Automatic insertion of transaction to fulfill the following assertion:
### Events
### Options See the [Beancount Options Reference](http://furius.ca/beancount/doc/options) for the full list of supported options.
### Other
././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/help/budgets.md0000644000175000001440000000275100000000000016453 0ustar00jakobusers00000000000000Budgets on a per-account basis can be added via `custom` directives in the Beancount file:
If budgets are specified, Fava's reports and charts will display remaining budgets and related information. The budget directives can be specified `daily`, `weekly`, `monthly`, `quarterly` and `yearly`. The specified budget is valid until another budget directive for the account is specified. The budget is broken down to a daily budget, and summed up for a range of dates as needed. This makes the budgets very flexible, allowing for a monthly budget, being taken over by a weekly budget, and so on. Fava displays budgets in both charts and reports. You can find a visualization of the global budget in the the `Net Profit` and `Expenses` charts for the `Income Statement` report. The `Expenses` `Income Statement` report is a good starting point for getting access to the full budget information in Fava. The `Changes` charts visualize the data. The `Changes (monthly)` and `Balances (monthly)` reports show, respectively, the monthly and cumulative (over the selected period) budgets. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/help/extensions.md0000644000175000001440000000442600000000000017216 0ustar00jakobusers00000000000000Fava supports extensions. Extensions allow you to register hooks and generate your own report pages. If you use this extension system and need it to do more or need other hooks, please open an issue on [GitHub](https://github.com/beancount/fava/issues). A Fava extension is simply a Python module which contains a class that inherits from `FavaExtensionBase` from `fava.ext`. Invoking an extension is done via the `fava-extension` option in the beancount file. Check out `fava.ext.auto_commit` for an example. Extensions may also contain a report - this is detected when the extension's class has a `report_title` attribute. The template for the report should be in a `templates` subdirectory with a report matching the class's name. For example, check out `fava.ext.portfolio_list` which has its template located at `fava/ext/portfolio_list/templates/PortfolioList.html`. The whole extension system should be considered unstable and it might change drastically. ## Fava Extension Setup Options --- ## `fava-extension` A Python module to load as extension. The path of the Beancount file is searched in addition to anything on the Python path. Single python files will also be searched - so for example a `my_extension.py` could be used by giving `my_extension`. Note that Python has a global namespace for currently loaded modules, so try avoiding simple names that might coincide with some Python library (as well as running Fava on two files that have different extensions of the same name). Extensions allow for an optional configuration options string, whose structure is specified by the individual extension.
--- ## Hooks Below is a list of all current hooks. ### `after_write_source(path, source)` Called after the string `source` has been written to the Beancount file at `path`. --- ### `after_insert_metadata(entry, key, value)` Called after metadata (`key: value`) has been added to an `entry`. --- ### `after_insert_entry(entry)` Called after an `entry` has been inserted. --- ## Extension attributes ### `report_title` Optional attribute to set extension report title used in the sidebar. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1581854591.0 fava-1.14/fava/help/features.md0000644000175000001440000001453700000000000016641 0ustar00jakobusers00000000000000This is an overview of some of the more advanced features that Fava has to offer. ## Editor The [editor]({{ url_for('report', report_name='editor') }}) provides a convenient way to edit the source file. The cursor will jump to the bottom of the file by default, or if the string `FAVA-INSERT-MARKER` is found in the file, to the line above it. If you want to use a file different from the main file to be opened by default, use the `default-file` option. The editor supports auto-completion for account names and tags. Trailing whitespace is highlighted in red. ## Queries On the [Query]({{ url_for('report', report_name='query') }}) report you can execute queries like with the `bean-query` command-line tool. For an explanation of how these queries work see the [Beancount Query Language Reference](http://furius.ca/beancount/doc/query). Fava displays charts for BQL queries - if they have exactly two columns with the first being a date or string and the second an inventory, then a line chart or treemap chart is shown on the query page. Fava supports downloading the result of these queries in various file formats. By default, only exporting to `csv` is supported. For support of `xls`, `xlsx` and `ods`, install Fava with the `excel` feature: pip3 install fava[excel] ## Adding Transactions By clicking the `+` button or using the `n` keyboard shortcut you can open a form to insert a transaction to your Beancount file. The position that transactions are inserted at can be specified in a flexible way using the `insert-entry` option. If you want to set a bookmark to this form, adding `#add-transaction` to any URL in Fava will open it on load. Tags and links can be added in the form by adding them (separated by spaces) to the narration field, e.g., `narration #tag ^somelink`. ## Up-to-date indicators Fava offers colored indicators that can help you keep your accounts up-to-date. They are shown next to accounts that have the metadata `fava-uptodate-indication: TRUE` set on their Open directive. The colors have the following meaning: - green: The last entry for this account is a balance check that passed. - red: The last entry is a balance check that failed. - yellow: The last entry is not a balance check. In addition, a grey dot will be shown if the account has not been updated in a while, as configured by the `uptodate-indicator-grey-lookback-days` option. ## Displaying only relevant accounts To help display only the most relevant subset of accounts when managing a large number or a deep hierarchy of accounts, Fava offers the following options: - `show-closed-accounts` - `show-accounts-with-zero-balance` - `show-accounts-with-zero-transactions` - `collapse-pattern` ## Opening an external editor Fava can open up your source file in your favorite editor directly from the web interface using the `use-external-editor` configuration variable through the `beancount://` URL handler. See the [Beancount urlscheme](https://github.com/aumayr/beancount_urlscheme) project for pre-configured URL handlers for macOS and Cygwin. ## Multiple Beancount files When you start Fava specifying multiple Beancount files, you can click the Beancount file name on the top left to switch between the files. ## Custom links in the sidebar If you regularly use certain views in Fava with different filters, you can put links to them in the sidebar. Custom links can be put in the Beancount file, utilizing the `custom` directive: 2016-05-04 custom "fava-sidebar-link" "Income 2014" "../income_statement?time=2014" `"fava-sidebar-link"` specifies that this directive is for a custom sidebar link, followed by the title to display in the sidebar (`"Income 2014"` in this example), and finally the URL to link to. The URL can be relative, like in the example above, or absolute, even linking to an external site. Two frequently used custom links are for showing all Documents and all Notes found in the journal: - For all Documents: `//journal/?show=document` - For all Notes: `//journal/?show=note` There is a special URL handler `/jump` which can be used to jump to the current page with given URL parameters. For example, `/jump?time=month` will show the current page but change the time filter to the current month. ## Language You can change the language of the interface by specifying the `language` option. If no option is specified, Fava tries to guess the language from your browser settings. ## Documents upload One or more documents can be uploaded by drag and drop on account names or Journal rows. ### Uploading documents To store a document in a specific account, just drag and drop the file on the account name in a tree-table. While still dragging, the background-color of the account name will switch to blue, indicating that you can drop a file there. Once dropped, a popup will be shown where you can rename the file before storing. If the filename does not already start with a date (`YYYY-MM-DD`), the current date will be added as a prefix automatically. The file will be then stored in your Beancount documents folder, in a sub-folder named after the account. You can set the path to the Beancount documents folder by specifying the `option "documents" "/Users/test/invoices"`-option (absolute or relative to your Beancount file) in your Beancount file. Beancount will automatically discover files in your `"documents"`-folders and generate Document entries for them. When enabling the `tag_discovered_documents`-plugin, these Document entries will be tagged with `#discovered` and can be filtered in the Journal: plugin "fava.plugins.tag_discovered_documents" ### Uploading statements When dropping a file on a transaction (or one of its postings) in the Journal, the file will be uploaded as described above, and a `document`-metadata-entry inserted for the transaction in your Beancount file. When dropped on the description the subfolder corresponds to the account of the first posting. **Note**: Uploading statements modifies your Beancount file! When enabling the `link_documents`-plugin, the Document entries created by Beancount (see above) will be tagged with `#linked`, linked to the corresponding transaction and can be filtered in the Journal: plugin "fava.plugins.link_documents" ### Exporting a Journal view When displaying a Journal, including a filtered Journal, the entries displayed can be downloaded in Beancount format by clicking the `Export` button. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/help/filters.md0000644000175000001440000000600400000000000016461 0ustar00jakobusers00000000000000With the text inputs at the top right of the page, you can filter the entries that are displayed in Fava's reports. If you use multiple filters, the entries matching all of them will be selected. ### Time Filter entries by their date. You can specify dates and intervals like years, quarters, months, weeks, and days (for example `2015`, `2012-Q1`, `2010-10`, `2016-W12`, or `2015-06-12`). You can specify a range of dates like `2010 - 2012-10` which will display all entries between the start of 2010 and the end of October 2012. To refer to dates relative to the current day, you can use the variables `year`, `quarter`, `month`, `week`, and `day`. These will be substituted with the current date expressed in the respective format, and support addition and subtraction. For example you can write `year - day` for all entries in the current year up to today, or `year-1 - year` for all entries of the last and current year. To prevent subtraction, use parentheses: `(month)-10` refers to the 10th of this month, whereas `month-10` would be 10 months ago. ### Account Filter entries by account, matching any entry this account is part of. The filter can be an account name, either the full account name or a component of the account name, or a regular expression matching the account name, e.g. `.*Company.*` to filter for all that contain `Company`. ### Filter by tag, link, payee and other metadata This final filter allows you to filter entries by various attributes. - Filter by `#tag` or `^link`. - Filter by payee `payee:".*restaurant.*"`, or narration `narration:'Dinner with Joe'`, or any other entry attribute. The argument needs to be quoted (with `'` or `"`) if it contains spaces and can either be simply a string or a regular expression that matches the attribute. The regular expression matching is case insensitive. - Search payee and narration by supplying a simple string, which again is matched as a regular expression. For Note directives, the comment will be searched. - Filter for entries having certain metadata values: `statement:".*pdf"`. Note that if the entry has an attribute of the same name as the metadata key, the filter will apply to the entry attribute, not the metadata value. - Exclude entries that match a filter by prepending a `-` to it, e.g. `-#tag` or `-(^link #tag)`. - To match entries by posting attributes, you can use `any()` and `all()`, e.g., `any(id:'12', account:".*Cash")` for all entries that have at least one posting with metadata `id: 12` or account ending in `Cash`, or `all(-account:"Expenses:Food")` to exclude all transactions having a posting to the Expenses:Food account. These filters can be combined by separating them by spaces to match all entries satisfying all given filters or by commas to match all entries satisfying at least one of the given filters. In other words, a space acts like an "and" and a comma like an "or". As usual, the logical "and" has a higher grouping power than "or" and you can use parentheses to group filters. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/help/import.md0000644000175000001440000000141300000000000016322 0ustar00jakobusers00000000000000Fava can use Beancount's import system to semi-automatically import entries into your Beancount ledger. See [Importing External Data in Beancount](http://furius.ca/beancount/doc/ingest) for more information on how to write importers. Set the `import-config` option to point to your import config and set `import-dirs` to the directories that contain the files that you want to import. And if you wish to save new entries elsewhere than main file - use `insert-entry` option. Fava currently only supports entries of type `Transaction` and `Balance`. Set the special metadata key `__source__` to display the corresponding text (CSV-row, XML-fragment, etc.) for the entry in the list of entries to import. Note that this metadata will be stripped before saving the entry to file. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1581494646.0 fava-1.14/fava/help/options.md0000644000175000001440000001544500000000000016515 0ustar00jakobusers00000000000000To customize some of Fava's behaviour, you can add custom entries like the following to your Beancount file.
Below is a list of all possible options for Fava. --- ## `language` Default: Not set If this setting is not specified, Fava will try to guess the language from your browser settings. Fava currently ships translations into the following languages: - Chinese (`zh_CN` and `zh_TW`) - Dutch (`nl`) - English (`en`) - French (`fr`) - German (`de`) - Persian (`fa`) - Portuguese (`pt`) - Russian (`ru`) - Slovak (`sk`) - Spanish (`es`) - Ukrainian (`uk`) --- ## `locale` Default: Not set or `en` if the Beancount `render_commas` option is set. This sets the locale that is used to render out numbers. For example, with the locale `en_IN` the number `1111111.33` will be rendered `11,11,111.33`, `1,111,111.33` with locale `en`, or `1.111.111,33` with locale `de`. --- ## `default-file` Use this option to specify a default file for the editor to open. This option takes no value, the file the custom entry is in will be used as the default. If this option is not specified, Fava opens the main file by default. --- ## `interval` Default: `month` The default interval that charts and the account reports by interval use. The possible options are `day`, `week`, `month`, `quarter`, and `year`. --- ## `fiscal-year-end` Default: `12-31` The last day of the fiscal (financial or tax) period for accounting purposes in `%m-%d` format. Allows for the use of `FY2018`, `FY2018-Q3`, `fiscal_year` and `fiscal_quarter` in the time filter. Examples are: - `09-30` - US federal government - `06-30` - Australia / NZ - `04-05` - UK See [Fiscal Year on WikiPedia](https://en.wikipedia.org/wiki/Fiscal_year) for more examples. --- ## `insert-entry` Default: Not set. This option can be used to specify where entries are inserted. The argument to this option should be a regular expression matching account names. This option can be given multiple times. When adding an entry, the account of the entry (for a transaction, the account of the last posting is used) is matched against all `insert-entry` options and the entry will be inserted before the datewise latest of the matching options. If the entry is a Transaction and no `insert-entry` option matches the account of the last posting the account of the second to last posting and so on will be tried. If no `insert-entry` option matches or none is given, the entry will be inserted at the end of the main file. --- ## `auto-reload` Default: `false` Set this to `true` to make Fava automatically reload the page whenever a file changes is detected. By default only a notification is shown which you can click to reload the page. If the file change is due to user interaction, e.g., uploading a document or adding a transaction, Fava will always reload the page automatically. --- ## `unrealized` Default: `Unrealized` The subaccount of the Equity account to post unrealized gains to if the account trees are shown at market value. --- ## `journal-show` Default: `transaction balance note document custom budget query` The entry types and other elements given in this list will be shown in the Journal report. Supported values are entry type names, as well as well as the special values "metadata" and "postings" that determine the default visibility of the corresponding transaction parts. All other elements will be hidden and can be toggled using the buttons. --- ## `journal-show-transaction` Default: `cleared pending` Similarly to the `journal-show` setting, this determines the transaction types that will be shown in the Journal report. The "transaction types" correspond to the following transaction flags: - `cleared` - `*` - `pending` - `!` - `other` - All other transaction flags. --- ## `currency-column` Default: `61` This option can be used to configure how posting lines are aligned when saved to file or when using 'Align Amounts' in the editor. Fava tries to align so that the currencies all occur in the given column. Also, Fava will show a vertical line before this column in the editor. --- ## `sidebar-show-queries` Default: `5` The maximum number of queries to link to in the sidebar. Set this value to `0` to hide the links altogether. --- ## `upcoming-events` Default: `7` Show a notification bubble in the sidebar displaying the number of events less than `upcoming-events` days away. Set this value to `0` to disable this feature. --- ## `show-closed-accounts` Default: `false` ## `show-accounts-with-zero-transactions` Default: `true` ## `show-accounts-with-zero-balance` Default: `true` These three options options specify which accounts (not) to show in the account trees, like on the income statement. Accounts with a non-zero balance will always be shown. --- ## `collapse-pattern` Default: Not set This option is used to specify accounts that will be collapsed in the displayed account trees. The argument to this option is a regular expression matching account names. This option can be specified multiple times. Collapsing all accounts below a specific depth in the account tree can be accomplished by a regex such as: `.*:.*:.*` (this example collapses all accounts that are three levels deep). --- ## `use-external-editor` Default: `false` If `true`, instead of using the internal editor, the `beancount://` URL scheme is used. See the [Beancount urlscheme](https://github.com/aumayr/beancount_urlscheme) project for details. --- ## `account-journal-include-children` Default: `true` This determines if the journal in the account report includes entries of sub-accounts. --- ## `uptodate-indicator-grey-lookback-days` Default: `60` If there has been no activity in given number of days since the last balance entry, then the grey uptodate-indicator is shown. --- ## `import-config` Default: Not set Path to a Beancount import configuration file. See the [Import]({{ url_for('help_page', page_slug='import') }}) help page for details. --- ## `import-dirs` Default: Not set Set the directories to be scanned by the Beancount import mechanism. --- ## `conversion` Default: "at_cost" The default conversion that charts and the account reports use. This option takes a string representing the same values available in the conversion dropdown. Examples are: - `at_cost` - At Cost - `at_value` - At Market Value - `units` - Units - `USD` - Converted to USD - `EUR` - Converted to EUR If this option is not specified or the value is not valid (i.e., a non-existent commodity), Fava defaults to "At Cost" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1581854595.0 fava-1.14/fava/json_api.py0000644000175000001440000001644300000000000015723 0ustar00jakobusers00000000000000"""JSON API. This module contains the url endpoints of the JSON API that is used by the web interface for asynchronous functionality. """ import functools import os import shutil from os import path from flask import Blueprint from flask import g from flask import get_template_attribute from flask import jsonify from flask import request from fava.core.file import save_entry_slice from fava.core.helpers import FavaAPIException from fava.core.misc import align from fava.serialisation import deserialise from fava.serialisation import serialise json_api = Blueprint("json_api", __name__) # pylint: disable=invalid-name def get_api_endpoint(func): """Register a GET endpoint.""" @json_api.route("/{}/".format(func.__name__), methods=["GET"]) @functools.wraps(func) def _wrapper(*args, **kwargs): return jsonify({"success": True, "data": func(*args, **kwargs)}) return _wrapper def put_api_endpoint(func): """Register a PUT endpoint.""" @json_api.route("/{}/".format(func.__name__), methods=["PUT"]) @functools.wraps(func) def _wrapper(*args, **kwargs): request_data = request.get_json() if request_data is None: raise FavaAPIException("Invalid JSON request.") res = func(request_data, *args, **kwargs) return jsonify({"success": True, "data": res}) return _wrapper def json_response(func): """Jsonify the response.""" @functools.wraps(func) def _wrapper(*args, **kwargs): json_data = func(*args, **kwargs) if "success" not in json_data: json_data["success"] = True return jsonify(json_data) return _wrapper def json_request(func): """Check existence and load the JSON payload of the request.""" @functools.wraps(func) def _wrapper(): request_data = request.get_json() if request_data is None: raise FavaAPIException("Invalid JSON request.") return func(request_data) return _wrapper @json_api.errorhandler(FavaAPIException) @json_response def _json_api_exception(error): return {"success": False, "error": error.message} @json_api.errorhandler(OSError) @json_response def _json_api_oserror(error): return {"success": False, "error": error.strerror} @get_api_endpoint def changed(): """Check for file changes.""" return g.ledger.changed() @get_api_endpoint def errors(): """Number of errors.""" return len(g.ledger.errors) @get_api_endpoint def payee_accounts(): """Rank accounts for the given payee.""" return g.ledger.attributes.payee_accounts(request.args.get("payee")) @get_api_endpoint def query_result(): """Render a query result to HTML.""" query = request.args.get("query_string", "") table = get_template_attribute("_query_table.html", "querytable") contents, types, rows = g.ledger.query_shell.execute_query(query) if contents: if "ERROR" in contents: raise FavaAPIException(contents) table = table(contents, types, rows) if types and g.ledger.charts.can_plot_query(types): return { "table": table, "chart": g.ledger.charts.query(types, rows), } return {"table": table} @get_api_endpoint def extract(): """Extract entries using the ingest framework.""" entries = g.ledger.ingest.extract( request.args.get("filename"), request.args.get("importer") ) return list(map(serialise, entries)) @get_api_endpoint def move(): """Move a file.""" if not g.ledger.options["documents"]: raise FavaAPIException("You need to set a documents folder.") account = request.args.get("account") new_name = request.args.get("newName") filename = request.args.get("filename") new_path = filepath_in_document_folder( g.ledger.options["documents"][0], account, new_name ) if not path.isfile(filename): raise FavaAPIException("Not a file: '{}'".format(filename)) if path.exists(new_path): raise FavaAPIException("Target file exists: '{}'".format(new_path)) if not path.exists(path.dirname(new_path)): os.makedirs(path.dirname(new_path), exist_ok=True) shutil.move(filename, new_path) return "Moved {} to {}.".format(filename, new_path) @get_api_endpoint def payee_transaction(): """Last transaction for the given payee.""" entry = g.ledger.attributes.payee_transaction(request.args.get("payee")) return serialise(entry) @put_api_endpoint def source(request_data): """Write one of the source files and return the updated sha256sum.""" if request_data.get("file_path"): sha256sum = g.ledger.file.set_source( request_data.get("file_path"), request_data.get("source"), request_data.get("sha256sum"), ) else: entry = g.ledger.get_entry(request_data.get("entry_hash")) sha256sum = save_entry_slice( entry, request_data.get("source"), request_data.get("sha256sum") ) return sha256sum @put_api_endpoint def format_source(request_data): """Format beancount file.""" return align(request_data["source"], g.ledger.fava_options) def filepath_in_document_folder(documents_folder, account, filename): """File path for a document in the folder for an account. Args: documents_folder: The documents folder. account: The account to choose the subfolder for. filename: The filename of the document. Returns: The path that the document should be saved at. """ if documents_folder not in g.ledger.options["documents"]: raise FavaAPIException( "Not a documents folder: {}.".format(documents_folder) ) if account not in g.ledger.attributes.accounts: raise FavaAPIException("Not a valid account: '{}'".format(account)) for sep in os.sep, os.altsep: if sep: filename = filename.replace(sep, " ") return path.normpath( path.join( path.dirname(g.ledger.beancount_file_path), documents_folder, *account.split(":"), filename ) ) @json_api.route("/add-document/", methods=["PUT"]) @json_response def add_document(): """Upload a document.""" if not g.ledger.options["documents"]: raise FavaAPIException("You need to set a documents folder.") upload = request.files["file"] if not upload: raise FavaAPIException("No file uploaded.") filepath = filepath_in_document_folder( request.form["folder"], request.form["account"], upload.filename ) directory, filename = path.split(filepath) if path.exists(filepath): raise FavaAPIException("{} already exists.".format(filepath)) if not path.exists(directory): os.makedirs(directory, exist_ok=True) upload.save(filepath) if request.form.get("hash"): g.ledger.file.insert_metadata( request.form["hash"], "document", filename ) return {"data": "Uploaded to {}".format(filepath)} @put_api_endpoint def add_entries(request_data): """Add multiple entries.""" try: entries = [deserialise(entry) for entry in request_data["entries"]] except KeyError as error: raise FavaAPIException("KeyError: {}".format(str(error))) g.ledger.file.insert_entries(entries) return "Stored {} entries.".format(len(entries)) ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1581856610.615957 fava-1.14/fava/plugins/0000755000175000001440000000000000000000000015220 5ustar00jakobusers00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/plugins/link_documents.py0000644000175000001440000000514200000000000020612 0ustar00jakobusers00000000000000"""Beancount plugin to link transactions to documents. It goes through all transactions with a `document` metadata-key, and tries to associate them to Document entries. It then adds a link from transactions to documents, as well as the "#linked" tag. """ import collections from os.path import join, dirname, normpath, basename from beancount.core import data from beancount.core.compare import hash_entry DocumentError = collections.namedtuple("DocumentError", "source message entry") __plugins__ = ["link_documents"] def link_documents(entries, _): errors = [] all_documents = [ (index, entry) for index, entry in enumerate(entries) if isinstance(entry, data.Document) ] transactions = [ (index, entry) for index, entry in enumerate(entries) if isinstance(entry, data.Transaction) ] for index, entry in transactions: disk_docs = [ value for key, value in entry.meta.items() if key.startswith("document") ] _hash = hash_entry(entry)[:8] for disk_doc in disk_docs: disk_doc_path = normpath( join(dirname(entry.meta["filename"]), disk_doc) ) documents = [ (j, document) for j, document in all_documents if (document.filename == disk_doc_path) or ( document.account in [pos.account for pos in entry.postings] and basename(document.filename) == disk_doc ) ] if not documents: errors.append( DocumentError( entry.meta, "Document not found: {}".format(disk_doc), entry, ) ) continue for j, document in documents: tags = ( set(document.tags) .union(["linked"]) .difference(["discovered"]) if document.tags else set(["linked"]) ) links = ( set(document.links).union([_hash]) if document.links else set([_hash]) ) entries[j] = document._replace(links=links, tags=tags) links = ( set(entry.links).union([_hash]) if entry.links else set([_hash]) ) entries[index] = entry._replace(links=links) return entries, errors ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/plugins/tag_discovered_documents.py0000644000175000001440000000144300000000000022637 0ustar00jakobusers00000000000000"""Beancount plugin to tag discovered documents. It looks through all Document entries that were added by beancount automatically through file discovery and adds the tag "#discovered". """ from beancount.core import data __plugins__ = ["tag_discovered_documents"] def tag_discovered_documents(entries, options_map): errors = [] if "documents" not in options_map or not options_map["documents"]: return entries, errors for index, entry in enumerate(entries): if isinstance(entry, data.Document) and entry.meta["lineno"] == 0: tags = ( set(entry.tags).union(["discovered"]) if entry.tags else set(["discovered"]) ) entries[index] = entry._replace(tags=tags) return entries, errors ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1580847699.0 fava-1.14/fava/serialisation.py0000644000175000001440000001035600000000000016764 0ustar00jakobusers00000000000000"""(De)serialisation of entries. When adding entries, these are saved via the JSON API - using the functionality of this module to obtain the appropriate data structures from `beancount.core.data`. Similarly, for the full entry completion, a JSON representation of the entry is provided. This is not intended to work well enough for full roundtrips yet. """ import functools import re from beancount.core import data from beancount.core import position from beancount.core.amount import Amount from beancount.core.data import EMPTY_SET from beancount.core.number import D from beancount.parser.parser import parse_string from fava import util from fava.core.helpers import FavaAPIException def extract_tags_links(string): """Extract tags and links from a narration string. Args: string: A string, possibly containing tags (`#tag`) and links (`^link`). Returns: A triple (new_string, tags, links) where `new_string` is `string` stripped of tags and links. """ if string is None: return None, EMPTY_SET, EMPTY_SET tags = re.findall(r"(?:^|\s)#([A-Za-z0-9\-_/.]+)", string) links = re.findall(r"(?:^|\s)\^([A-Za-z0-9\-_/.]+)", string) new_string = re.sub(r"(?:^|\s)[#^]([A-Za-z0-9\-_/.]+)", "", string).strip() return new_string, frozenset(tags), frozenset(links) @functools.singledispatch def serialise(entry): """Serialise an entry.""" if not entry: return None ret = entry._asdict() ret["type"] = entry.__class__.__name__ if ret["type"] == "Transaction": ret["payee"] = entry.payee or "" if entry.tags: ret["narration"] += " " + " ".join(["#" + t for t in entry.tags]) if entry.links: ret["narration"] += " " + " ".join(["^" + l for l in entry.links]) del ret["links"] del ret["tags"] ret["postings"] = [serialise(pos) for pos in entry.postings] elif ret["type"] == "Balance": amt = ret["amount"] ret["amount"] = {"number": str(amt.number), "currency": amt.currency} return ret @serialise.register(data.Posting) def _serialise_posting(posting): """Serialise a posting.""" if isinstance(posting.units, Amount): position_str = position.to_string(posting) else: position_str = "" if posting.price is not None: position_str += " @ {}".format(posting.price.to_string()) return {"account": posting.account, "amount": position_str} def deserialise_posting(posting): """Parse JSON to a Beancount Posting.""" amount = posting.get("amount", "") entries, errors, _ = parse_string( '2000-01-01 * "" ""\n Assets:Account {}'.format(amount) ) if errors: raise FavaAPIException("Invalid amount: {}".format(amount)) pos = entries[0].postings[0] return pos._replace(account=posting["account"], meta=None) def deserialise(json_entry): """Parse JSON to a Beancount entry. Args: json_entry: The entry. Raises: KeyError: if one of the required entry fields is missing. FavaAPIException: if the type of the given entry is not supported. """ if json_entry["type"] == "Transaction": date = util.date.parse_date(json_entry["date"])[0] narration, tags, links = extract_tags_links(json_entry["narration"]) postings = [deserialise_posting(pos) for pos in json_entry["postings"]] return data.Transaction( json_entry["meta"], date, json_entry.get("flag", ""), json_entry.get("payee", ""), narration, tags, links, postings, ) if json_entry["type"] == "Balance": date = util.date.parse_date(json_entry["date"])[0] raw_amount = json_entry["amount"] amount = Amount(D(str(raw_amount["number"])), raw_amount["currency"]) return data.Balance( json_entry["meta"], date, json_entry["account"], amount, None, None ) if json_entry["type"] == "Note": date = util.date.parse_date(json_entry["date"])[0] comment = json_entry["comment"].replace('"', "") return data.Note( json_entry["meta"], date, json_entry["account"], comment ) raise FavaAPIException("Unsupported entry type.") ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1581856610.5959573 fava-1.14/fava/static/0000755000175000001440000000000000000000000015026 5ustar00jakobusers00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1581856610.6192904 fava-1.14/fava/static/gen/0000755000175000001440000000000000000000000015577 5ustar00jakobusers00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1581856590.0 fava-1.14/fava/static/gen/app.css0000644000175000001440000014041700000000000017100 0ustar00jakobusers00000000000000.chart-labels { margin-bottom: 1.5em; font-size: 1em; color: var(--color-text-lightest); text-align: center; } .chart-labels label { padding-left: 0.5em; cursor: pointer; } .chart-labels label + label { border-left: 1px solid var(--color-text-lighter); } .chart-labels label.selected, .chart-labels label:hover { color: var(--color-text-lighter); } .chart-mode span { margin-right: 0; color: var(--color-text); background-color: var(--color-background-darker); } .chart-mode input { display: none; } .chart-mode span:hover { color: var(--color-background); } .chart-mode input:checked + span { color: var(--color-background); background-color: var(--color-header); } .toggle-chart { height: 22px; padding: 2px 6px; margin: 0; } .toggle-chart::before { display: block; content: ""; border: 0; border-top: 13px solid var(--color-background); border-right: 9px solid transparent; border-left: 9px solid transparent; } .toggle-chart.closed::before { border: 0; border-right: 9px solid transparent; border-bottom: 13px solid var(--color-background); border-left: 9px solid transparent; } .chart-legend { flex-grow: 1; } .chart-legend .color { display: inline-block; width: 10px; height: 10px; margin-left: 5px; border-radius: 10px; } .chart-legend .legend { display: inline-block; } svg text { fill: var(--color-text-lightest); } svg .line { fill: none; stroke-width: 2px; } svg .axis path, svg .axis line { fill: none; stroke: var(--color-chart-axis); shape-rendering: crispEdges; } svg .axis.y line { stroke: var(--color-chart-axis); stroke-dasharray: 2, 2; } svg.scatterplot > g, svg.linechart > g { pointer-events: all; } svg.treemap { shape-rendering: crispEdges; } svg.treemap rect { stroke: var(--color-treemap-text); stroke-width: 2px; } svg.treemap text { cursor: pointer; } svg.sunburst .account { fill: var(--color-text); } svg.sunburst .balance { font-family: var(--font-family-monospaced); } svg.sunburst path { cursor: pointer; } svg.barchart .axis-group-box { cursor: pointer; opacity: 0; } svg.barchart .group-box { opacity: 0; } svg.barchart .group:hover .group-box { opacity: 0.1; } svg.barchart .budget { opacity: 0.3; } .tooltip { position: absolute; z-index: var(--z-index-floating-ui); min-width: 5em; padding: 0.5em; font-family: var(--font-family-monospaced); text-align: center; pointer-events: none; background: var(--color-background); border: 1px solid var(--color-background-darkest); box-shadow: 0 0 5px var(--color-background-darker); opacity: 0; transform: translate(-50%, -100%); } .tooltip::before { position: absolute; top: 100%; left: 50%; width: 0; height: 0; margin-left: -15px; content: ""; border: 15px solid transparent; border-top-color: var(--color-background-darkest); } .tooltip em { display: block; margin-top: 5px; font-family: var(--font-family); color: var(--color-text-lightest); } details, .toggle-box { display: block; min-width: 400px; margin-bottom: 0.5em; border: 1px solid var(--color-table-border); } details.inactive, .toggle-box.inactive { opacity: 0.5; } details summary, .toggle-box-header { display: flex; align-items: center; padding: 5px 10px; cursor: pointer; background-color: var(--color-sidebar-background); } details summary::before, .toggle-box-header::before { margin-right: 0.5em; content: ""; border-top: 9px solid var(--color-treetable-expander); border-right: 6px solid transparent; border-left: 6px solid transparent; } details.error summary, .toggle-box.error .toggle-box-header { background-color: var(--color-error); } details summary::before { transform: rotate(270deg); } details[open] summary::before { transform: rotate(0deg); } .toggle-box.toggled .toggle-box-header::before { transform: rotate(270deg); } details summary pre, .toggle-box-header pre { display: inline-block; padding: 3px 6px; margin: 0; } details > div, .toggle-box-content { padding: 0.5em; } .toggle-box.toggled .toggle-box-content { display: none; } .entry-form { margin-bottom: 0.5rem; } .entry-form textarea { flex-grow: 1; width: 100%; padding: 8px; font: inherit; } .entry-form label { display: none; } .entry-form h4 { margin: 0; } .entry-form .amount { width: 220px; } .entry-form button, .entry-form input { margin: 0; } .entry-form > * + * { margin-top: 0.5rem; } .entry-form .fieldset > * + * { margin-left: 0.5rem; } .entry-form input[name="flag"] { width: 1.5em; padding-right: 2px; padding-left: 2px; text-align: center; } .entry-form .account, .entry-form input[name="narration"] { flex-basis: 200px; flex-grow: 1; min-width: 20em; } .entry-form .payee { flex-basis: 100px; flex-grow: 1; min-width: 10em; } .entry-form input.metadata-value { flex-grow: 1; max-width: 15em; } .balance .currency { width: 6em; } .entry-form input.metadata-key { width: 10em; } .entry-form .remove-fieldset { opacity: 0; } .entry-form .fieldset:hover .remove-fieldset { opacity: 1; } .entry-form .posting .add-row { display: none; } .entry-form .posting:last-child .add-row { display: initial; } .entry-form .posting { padding-left: 50px; cursor: grab; } .entry-form .posting > * { cursor: initial; } .entry-form .posting:last-child .amount { width: 192px; } .entry-form .metadata { padding-left: 56px; font-size: 0.8em; } .ingest-row .source pre { font-size: 0.9em; white-space: pre-wrap; } .ingest-row.duplicate { opacity: 0.5; } @media (max-width: 767px) { .entry-form.transaction label { display: initial; width: 100%; } .entry-form.transaction .fieldset input { height: 35px; margin: 0 0.5em 0.5em 0; } .entry-form.transaction .metadata, .entry-form.transaction .posting { padding-left: 0; } } /* For the editor */ @font-face { font-family: "Source Code Pro"; font-style: normal; font-weight: normal; src: url("./source-code-pro-all-400.woff2") format("woff2"); } @font-face { font-family: "Source Code Pro"; font-style: normal; font-weight: 500; src: url("./source-code-pro-all-500.woff2") format("woff2"); } /* Interface fonts */ @font-face { font-family: "Fira Mono"; font-style: normal; font-weight: normal; src: url("./fira-mono-all-400.woff2") format("woff2"); } @font-face { font-family: "Fira Mono"; font-style: normal; font-weight: 500; src: url("./fira-mono-all-500.woff2") format("woff2"); } @font-face { font-family: "Fira Sans"; font-style: normal; font-weight: normal; src: url("./fira-sans-all-400.woff2") format("woff2"); } @font-face { font-family: "Fira Sans"; font-style: normal; font-weight: 500; src: url("./fira-sans-all-500.woff2") format("woff2"); } /* The help pages */ @font-face { font-family: "Source Serif Pro"; font-style: normal; font-weight: normal; src: url("./source-serif-pro-latin-400.woff2") format("woff2"); } @font-face { font-family: "Source Serif Pro"; font-style: normal; font-weight: 600; src: url("./source-serif-pro-latin-600.woff2") format("woff2"); } /* stylelint-disable no-descending-specificity */ * { box-sizing: border-box; } html, body { margin: 0; font-family: var(--font-family); font-size: 14px; font-weight: 400; line-height: 1.5; color: var(--color-text); background-color: var(--color-background); } p, ol, ul, dl, table, pre, hr { padding: 0; margin: 0 0 1rem; list-style-type: none; } dl { margin: 0; } code, pre { font-family: var(--font-family-monospaced); white-space: pre; background-color: var(--color-code-background); border: 1px solid var(--color-background-darker); border-radius: 3px; } code { padding: 0 4px; line-height: 1; } pre { padding: 6px 10px; overflow: auto; } pre code { padding: 0; margin: 0; line-height: inherit; border: 0; } table { border-spacing: 0; border-collapse: collapse; } td, th { padding: 2px 5px; white-space: nowrap; } td.num, th.num { width: 7em; font-family: var(--font-family-monospaced); color: var(--color-text); text-align: right; } thead th, tfoot td { font-weight: 400; color: var(--color-table-header-text); background-color: var(--color-table-header-background); border: 1px solid var(--color-table-header-background); } tbody tr:nth-child(2n) { background-color: var(--color-sidebar-background); } tbody td { border: 1px solid var(--color-table-border); } table input { margin: 0; } table pre { padding: 0; margin: 0; overflow: inherit; background-color: inherit; border: 0; } h2, h3, h4, h5 { padding: 0; margin: 0 0.5rem 1rem 0; font-weight: 500; color: var(--color-headings); } h2 { font-size: 1.2857em; } h3 { font-size: 1.1429em; } h4, h5 { font-size: 1em; } hr { border: 1px solid var(--color-background-darker); } b, strong { font-weight: 500; } a { text-decoration: none; } a:hover, a:focus { color: var(--color-links-hover); } a:link, a:visited { color: var(--color-links); } a:active, a:focus, a img { border: 0; outline: none; } /* fake fieldsets to work around browser bugs with
*/ .fieldset { display: flex; flex-wrap: wrap; align-items: center; } button, input, textarea { margin: 0 0.5em 0.5em 0; font: inherit; } input, textarea { padding: 6px 10px; border: 1px solid var(--color-background-darkest); } label { margin: 0 0.5em 0.5em 0; } select { margin: 0 0.5em 0 0; font-size: inherit; } input:invalid { border: 1px solid var(--color-error); outline: none; box-shadow: none; } input[type="text"]::-webkit-calendar-picker-indicator { display: none; } input[type="date"]::-webkit-inner-spin-button, input[type="date"]::-webkit-clear-button { -webkit-appearance: none; display: none; } button, .button { padding: 6px 10px; color: var(--color-background); cursor: pointer; background-color: var(--color-header); border: 0; border-radius: 0; outline: 0; } h3 button { padding: 4px 8px; font-size: 1rem; font-weight: normal; } a.button { display: inline-block; padding: 2px 6px; } button:focus, button:active, button:hover, .button:focus, .button:active, .button:hover { background-color: var(--color-header-darker); box-shadow: 0 0 5px var(--color-header); } button:disabled, button.inactive, button.muted, .button:disabled, .button.inactive, .button.muted { color: var(--color-text); background-color: var(--color-background-darker); } button:disabled:focus, button:disabled:active, button:disabled:hover, button.inactive:focus, button.inactive:active, button.inactive:hover, button.muted:focus, button.muted:active, button.muted:hover, .button:disabled:focus, .button:disabled:active, .button:disabled:hover, .button.inactive:focus, .button.inactive:active, .button.inactive:hover, .button.muted:focus, .button.muted:active, .button.muted:hover { filter: brightness(90%); box-shadow: 0 0 5px var(--color-background-darker); } button.link, .button.link { padding: 0; margin: 0; color: var(--color-links); background: none; } button.link:focus, button.link:active, button.link:hover, .button.link:focus, .button.link:active, .button.link:hover { filter: brightness(90%); box-shadow: none; } button.round, .button.round { height: 1.5em; padding: 0 0.5em; border-radius: 15px; } .button:link, .button:visited { height: 100%; color: var(--color-background); } .hidden.hidden { display: none; } /* Structural and generic elements */ :root { --header-height: 50px; --aside-width: 160px; --transitions: all 0.2s ease-out; } body { padding: var(--header-height) 0 0 var(--aside-width); } header { position: fixed; top: 0; left: 0; z-index: var(--z-index-header); display: flex; align-items: center; width: 100%; height: var(--header-height); padding: 0 7px 0 10px; color: var(--color-header-text); background-color: var(--color-header); } .aside-button { z-index: var(--z-index-floating-ui); display: none; background-color: var(--color-sidebar-background); } .aside-button:hover { background-color: var(--color-sidebar-background); } aside { position: fixed; top: var(--header-height); bottom: 0; left: 0; z-index: var(--z-index-aside); width: var(--aside-width); padding-top: 0.5rem; margin: 0; overflow-y: auto; color: var(--color-sidebar-text); background-color: var(--color-sidebar-background); border-right: 1px solid var(--color-sidebar-border); } .navigation { padding-bottom: 0.5rem; margin-bottom: 0.5rem; border-bottom: 1px solid var(--color-sidebar-border); } .navigation:last-child { margin-bottom: 0; border: none; } .navigation a { display: block; padding: 0.25em 0.5em 0.25em 1em; color: inherit; } .navigation a.selected, .navigation a:hover { color: var(--color-sidebar-text-hover); background-color: var(--color-sidebar-border); } .navigation .secondary { padding: 4px 9px 2px 9px; line-height: 23px; color: inherit; background-color: var(--color-sidebar-background); } .navigation .add-transaction-button { font-size: 23px; } .navigation .error { background-color: var(--color-error); } .navigation .error a { color: var(--color-sidebar-background); } .navigation li { position: relative; display: flex; flex-wrap: wrap; } .navigation li a:first-child { flex: 1; } .navigation .error a.selected, .navigation .error a:hover { background-color: var(--color-error); filter: brightness(80%); } .navigation .bubble { float: right; padding: 0 8px; font-size: 0.9em; color: var(--color-sidebar-text); background-color: var(--color-sidebar-border); border-radius: 12px; } .navigation .error .bubble { background-color: var(--color-transparent-white); } .submenu { width: 100%; } .submenu li { font-size: 0.95em; line-height: 0.95em; } .navigation .submenu a { padding-left: 35px; } .submenu a.selected, .submenu a:hover { filter: brightness(85%); } article { position: relative; width: 100%; height: 100%; padding: 1.5em; } .wide-form { display: flex; flex-wrap: wrap; align-items: center; width: 100%; margin-bottom: 0.5em; } .wide-form .small { margin-left: -6px; } .wide-form > span { margin-right: 0.5em; } .dragover { background-color: var(--color-links-transparent); } .headerline { display: flex; flex-wrap: wrap; align-items: center; margin: 0 0 1em; } .headerline h3 { display: inline-block; margin: 0 1.5em 0 0; } .headerline a { color: var(--color-text-lighter); } .headerline a:hover { color: var(--color-text); } kbd { display: inline-block; padding: 3px 6px; margin: 0 1px; font: 0.8em var(--font-family-monospaced); color: var(--color-text-lighter); background-color: var(--color-background); border: solid 1px var(--color-background-darker); border-bottom-color: var(--color-background-darkest); border-radius: 3px; box-shadow: inset 0 -1px 0 var(--color-background-darkest); } .keyboard-tooltip { position: absolute; z-index: var(--z-index-keyboard-overlays); display: inline-block; padding: 0.3em 0.5em; font-size: 0.9em; color: var(--color-background); text-align: center; background-color: var(--color-text); opacity: 0.9; } /* * Components */ .spacer { flex-grow: 1; } .row { display: flex; flex-wrap: wrap; padding: 5px; margin: -10px -20px; } .column { flex: 1; margin: 5px; } .column h3 { text-align: center; } .left { float: left; margin-right: 20px; } .right { float: right; } .status-indicator { display: inline-block; width: 6px; height: 6px; margin: 5px; border-radius: 6px; } td .status-indicator { float: right; margin-top: 3px; margin-left: 3px; } .status-indicator:hover { cursor: pointer; } .status-indicator.status-red { background-color: var(--color-status-red); } .status-indicator.status-yellow { background-color: var(--color-status-yellow); } .status-indicator.status-green { background-color: var(--color-status-green); } .status-indicator.status-gray { background-color: var(--color-status-gray); } .statistics-update-activity .indicator-header { padding-left: 0; } .statistics-update-activity .uptodate-indicator { text-align: center; } .statistics-update-activity .status-indicator { margin: 0 auto; } [data-sort] { position: relative; padding-right: 18px; cursor: pointer; } [data-order="desc"]::after { position: absolute; top: 12px; right: 4px; display: block; content: ""; border-top: 5px solid var(--color-text-lightest); border-right: 5px solid transparent; border-left: 5px solid transparent; } [data-order="asc"]::after { position: absolute; top: 10px; right: 4px; display: block; content: ""; border-right: 5px solid transparent; border-bottom: 5px solid var(--color-text-lightest); border-left: 5px solid transparent; } /* * View-specific and tables */ .options td { text-align: left; } .options td:nth-child(1) { font-weight: 500; } .options td:nth-child(2) { white-space: normal; } :root { --font-family: "Fira Sans", sans-serif; --font-family-alternative: "Source Serif Pro", sans-serif; --font-family-monospaced: "Fira Mono", monospace; --font-family-editor: "Source Code Pro", monospace; --z-index-aside: 1; --z-index-header: 2; --z-index-floating-ui: 3; --z-index-keyboard-overlays: 10; --z-index-autocomplete: 8; --z-index-overlay: 4; --color-transparent-black: hsla(0, 0%, 0%, 0.5); --color-transparent-white: hsla(0, 0%, 100%, 0.5); --color-background: #fff; --color-background-darker: #d9d9d9; --color-background-darkest: #ccc; --color-text: hsl(0, 0%, 27%); --color-text-lighter: hsl(0, 0%, 33%); --color-text-lightest: hsl(0, 0%, 47%); --color-links: hsl(203, 100%, 32%); --color-links-hover: hsl(203, 100%, 22%); --color-links-transparent: hsla(203, 100%, 32%, 0.5); --color-headings: #333; --color-error: hsl(0, 100%, 30%); --color-warning: hsl(52, 84%, 56%); --color-code-background: #f8f8f8; --color-sidebar-text: #444; --color-sidebar-text-hover: var(--color-links); --color-sidebar-background: hsl(0, 0%, 96%); --color-sidebar-border: hsl(0, 0%, 87%); --color-autocomplete-match: #ffd27c; --color-header: hsl(203, 100%, 32%); --color-header-darker: hsl(203, 100%, 22%); --color-header-light: hsl(203, 56%, 45%); --color-header-tinted: hsl(203, 47%, 66%); --color-header-text: hsl(0, 0%, 100%); --color-header-text-half: hsla(0, 0%, 100%, 0.5); --color-status-red: var(--color-error); --color-status-yellow: var(--color-warning); --color-status-green: hsl(151, 100%, 25%); --color-status-gray: #aaa; --color-notification: #fff; --color-notification-info: hsl(151, 100%, 25%); --color-notification-error: hsl(0, 100%, 30%); --color-notification-warning: hsl(52, 84%, 56%); --color-table-header-text: #666; --color-table-header-background: #e6e6e6; --color-table-border: #f2f2f2; --color-treetable-expander: #afc1d3; --color-budget-negative: #af3d3d; --color-budget-positive: #3daf46; --color-budget-zero: #ffb900; --color-treemap-text: #fff; --color-chart-axis: #999; --color-editor-comment: #998; --color-editor-trailing-whitespace: rgba(255, 199, 199, 0.5); --color-editor-directive: #333; --color-editor-class: #b84; --color-editor-date: #099; --color-editor-constant: #008080; --color-editor-account: var(--color-links); --color-editor-invalid: #333; --color-editor-activeline: #ffc; --color-mobile-button-text: #000; --color-ingest: #cefac1; } .journal .balance { --entry-type-color: #cfc; } .journal .close { --entry-type-color: hsl(0, 0%, 70%); } .journal .custom { --entry-type-color: #fff3ab; } .journal .document { --entry-type-color: #ffc8ff; } .journal .note { --entry-type-color: #aad0ff; } .journal .open { --entry-type-color: hsl(0, 0%, 92%); } .journal .other { --entry-type-color: #cff; } .journal .pad { --entry-type-color: #8ff; } .journal .pending { --entry-type-color: #f8a; } .journal .query { --entry-type-color: #aad0ff; } .journal .budget { --entry-type-color: #ffddae; } .journal { --color-journal-postings: hsl(0, 0%, 92%); --color-journal-metadata: hsl(210, 44%, 67%); --color-journal-tag: hsl(210, 61%, 64%); --color-journal-link: hsl(203, 39%, 85%); --color-journal-posting-indicator: hsl(203, 24%, 80%); --color-journal-metadata-indicator: hsl(203, 24%, 40%); } /* Help Pages */ :root { --color-help-sidebar: #f8f8f8; --color-help-sidebar-border: #eaeaea; --help-max-width: 600px; } .help { max-width: calc(var(--help-max-width) + 160px); } .help-text { max-width: var(--help-max-width); font: 16px var(--font-family-alternative); } .help-text h2, .help-text h3, .help-text h4, .help-text h5 { font-family: var(--font-family); } .help-text > div { margin-bottom: 1em; } .help-text .CodeMirror { height: auto; padding: 0; margin: 0; background-color: var(--color-code-background); border: 0; } .help-text code { font-size: 0.9em; } .help-text ul { padding-left: 2em; } .help-text li { list-style-type: disc; } .help-sidebar { float: right; padding: 10px 10px 0; margin: 0 0 10px 10px; font-size: 1.1em; background-color: var(--color-help-sidebar); border: 1px solid var(--color-help-sidebar-border); } .help-sidebar a:hover, .help-sidebar a.selected { font-weight: 500; } /* * At resolutions smaller than 768px, hide the aside menu. * This means that ipads and larger will show the side menu. */ @media (max-width: 767px) { body { padding: 0; transition: var(--transitions); } header { position: inherit; flex-wrap: wrap; height: auto; padding-left: 46px; } h1 { padding: 8px; } .filter-form { flex-wrap: wrap; } aside { top: 0; margin-left: calc(-1 * var(--aside-width)); transition: var(--transitions); } aside.active { margin-left: 0; } .aside-button.aside-button { transition: var(--transitions); } .navigation .secondary { transition: var(--transitions); } .aside-button, .navigation .add-transaction-button { position: fixed; left: 0; width: 42px; height: 42px; padding: 6px 4px; text-align: center; border-color: var(--color-sidebar-border); border-style: solid; border-width: 0 1px 1px 0; } .navigation .add-transaction-button { top: 42px; font-size: 30px; color: var(--color-mobile-button-text); } aside.active .add-transaction-button { left: var(--aside-width); } .aside-button { top: 0; display: block; margin-left: 0; } .aside-button.active { left: var(--aside-width); background-color: var(--color-sidebar-background); box-shadow: none; } .source-form, .source-editor-wrapper { left: 0; } } /* Overlays */ :root { --overlay-wrapper-background: rgba(0, 0, 0, 0.5); } .overlay { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: var(--z-index-overlay); display: none; } .overlay.shown { display: flex; align-items: center; justify-content: center; } .overlay-background { position: fixed; width: 100%; height: 100%; cursor: pointer; background: var(--overlay-wrapper-background); } .close-overlay { position: absolute; top: 1em; right: 1em; margin: 0; color: var(--color-text-lighter); } .overlay-content { position: relative; z-index: var(--z-index-overlay); display: flex; width: 100%; max-width: 767px; max-height: 100%; padding: 1em; margin: 0.5em; background: var(--color-background); box-shadow: 0 0 20px var(--overlay-wrapper-background); } .overlay-content form, .overlay-content > div { width: 100%; } .overlay strong { display: inline-block; margin: 6px 0 5px; } .overlay dt { display: inline-block; float: left; width: auto; padding-right: 10px; font-weight: normal; line-height: 1.8em; text-align: right; } .overlay dd { line-height: 1.8em; } .notifications { position: fixed; top: calc(var(--header-height) + 10px); right: 10px; width: 400px; } .notifications li { display: block; width: 100%; padding: 5px 10px; margin-bottom: 0.5em; color: var(--color-notification); background-color: var(--color-notification-info); } .notifications .error { background-color: var(--color-notification-error); } .notifications .warning { color: var(--color-text); background-color: var(--color-notification-warning); } /* Editor */ :root { --source-editor-fieldset-height: 44px; } .source-editor-wrapper { position: fixed; top: calc(var(--header-height) + var(--source-editor-fieldset-height)); right: 0; bottom: 0; left: var(--aside-width); } .dropdown { display: flex; height: 100%; margin: 0; } .dropdown .selected::before { content: "›"; } .dropdown > li { position: relative; height: var(--source-editor-fieldset-height); margin-right: 10px; line-height: var(--source-editor-fieldset-height); cursor: default; } .dropdown button { color: inherit; } .dropdown > li > ul { position: absolute; top: var(--source-editor-fieldset-height); z-index: var(--z-index-floating-ui); display: none; width: 500px; max-height: 400px; margin-left: -10px; overflow-y: auto; line-height: 1.5; background-color: var(--color-background); border: 1px solid var(--color-background-darker); border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; box-shadow: 0 3px 6px var(--color-transparent-black); } .dropdown > li > ul > li { padding: 2px 10px; } .dropdown > li > ul > li span { float: right; } .dropdown li:hover > ul { display: block; } .CodeMirror-gutters { background: var(--color-sidebar-background); border-right: 1px solid var(--color-sidebar-border); } .CodeMirror.CodeMirror { margin-bottom: 1em; font: 13px var(--font-family-editor); border: 1px solid var(--color-sidebar-border); } .source-form { position: fixed; top: var(--header-height); right: 0; bottom: 0; left: var(--aside-width); background: var(--color-sidebar-background); } .source-form .fieldset { height: var(--source-editor-fieldset-height); padding-left: 0.5em; border-bottom: 1px solid var(--color-sidebar-border); } .source-form .fieldset > button { margin: 0; } .source-form .CodeMirror-lines { border-top: 1px solid var(--color-sidebar-border); } .source-form .CodeMirror, .source-form textarea { width: 100%; height: 100%; margin: 0; border: 0; } .source-slice-editor-form .CodeMirror { height: auto; } .cm-trailingspace { background-color: var(--color-editor-trailing-whitespace); } .cm-section { padding-right: 10px; font-weight: 500; color: var(--color-editor-comment); border: solid 1px var(--color-editor-comment); border-radius: 2px; } .cm-comment { color: var(--color-editor-comment); } .cm-date { color: var(--color-editor-date); } .cm-directive { font-weight: 500; color: var(--color-editor-directive); } .cm-option { color: var(--color-editor-class); } .cm-account { color: var(--color-editor-account); } .cm-invalid { color: var(--color-editor-invalid); } .CodeMirror-hint { max-width: 600px; } .CodeMirror-hint .highlight { font-weight: 500; } .download, .stored-queries { color: var(--color-text-lighter); } .stored-queries label { margin-right: 8px; } .stored-queries a { margin-left: 6px; } .query-result > div { max-height: 80vh; overflow: auto; } .query-box { display: flex; align-items: center; padding-bottom: 1em; } .query-box button { margin: 0; } .query-box .CodeMirror { flex-grow: 1; width: 100%; height: auto; margin: 0; margin-right: 0.5em; font-family: var(--font-family-editor); font-size: 16px; border: 1px solid var(--color-background-darker); } .query-box .CodeMirror .CodeMirror-placeholder { color: #999; } .query-error { font-family: var(--font-family-monospaced); color: var(--color-background); background: var(--color-error); } .query-syntax-error { font-family: var(--font-family-monospaced); color: var(--color-error); } @media print { body { padding: 0; } header { position: relative; } header > * { display: none; } header h1 { display: block; } .filter-form { display: none; } aside, .aside-button, .toggle-chart-container { display: none; } } @keyframes spinner { to { transform: rotate(360deg); } } .fava-icon.loading { padding: 0; border-top: 2px solid var(--color-header-text); border-radius: 50%; animation: spinner 1s linear infinite; } .fava-icon.loading path { opacity: 0; } .beancount-files ul { max-height: 400px; margin-bottom: 0; overflow-y: auto; } .beancount-files a { display: block; padding: 8px 12px 8px 28px; cursor: pointer; } .beancount-files a.active, .beancount-files a:hover { color: var(--color-background); background-color: var(--color-links); } .beancount-files a.active { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAMAAABFNRROAAAAOVBMVEUAAAD///////////////////////////////////////////////////////////////////////8KOjVvAAAAEnRSTlMAAQMGHjE8P0Bfi6Omt9Hi7/XCzsNoAAAAO0lEQVQI143HSxZAMBAAwQ5CfEPf/7AWHmZH7Qr+aMcmZHONcQa6PV9ZEnBofsKg1jtQ9A2UGOinxKcT2psCetpsGdwAAAAASUVORK5CYII="); background-repeat: no-repeat; background-position: 9px center; } h1 { display: block; flex: 1; max-height: var(--header-height); padding: calc((var(--header-height) - 24px) / 2) 10px; margin: 0; overflow: hidden; font-size: 16px; font-weight: normal; color: var(--color-header-text); } h1 .droptarget { padding: 0.6em; margin-left: -0.6em; } /* stylelint-disable no-descending-specificity */ h1 a:hover, h1 a:link, h1 a:visited { color: inherit; } /* stylelint-enable no-descending-specificity */ h1 .last-activity { display: inline-block; margin-left: 10px; font-size: 12px; font-weight: normal; opacity: 0.8; } h1 .status-indicator { width: 10px; height: 10px; margin: 0 0 0 10px; border-radius: 10px; } h1 .status-indicator.status-gray { margin-left: 0; } .page-title::before { margin: 0 10px; font-weight: normal; color: var(--color-header-text-half); content: "›"; } .reload-page { padding-right: 12px; padding-left: 12px; margin-top: -8px; margin-left: 20px; background-color: var(--color-warning); } .beancount-files { position: absolute; top: var(--header-height); left: 19px; display: none; width: 20em; color: var(--color-links); background-color: var(--color-background); border: 1px solid var(--color-background-darker); border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; box-shadow: 0 3px 6px var(--color-transparent-black); } h1:hover .beancount-files { display: block; } .filter-form { display: flex; padding-top: 7px; margin: 0; color: var(--color-text); } .filter-form > span { position: relative; margin: 0 4px 6px 0; } .filter-form input { max-width: 18rem; padding: 8px 25px 8px 10px; margin: 0; background-color: var(--color-background); border: 0; outline: none; } .filter-form [type="text"]:focus { color: var(--color-text); background-color: var(--color-background); } .filter-form [type="text"]:placeholder-shown, .filter-form [type="text"]::placeholder { color: var(--color-header-tinted); opacity: 1; } .filter-form [type="text"]:focus:placeholder-shown, .filter-form [type="text"]:focus::placeholder { color: var(--color-header-tinted); } .filter-form [type="submit"] { display: none; } .filter-form .empty input { background-color: var(--color-header-light); } .filter-form .close { position: absolute; top: 8px; right: 0; display: block; } .filter-form .empty .close { display: none; } /* stylelint-disable no-descending-specificity */ .flex-table p, .flex-table li, .flex-table ul, .flex-table ol { padding: 0; margin: 0; } .flex-table p { display: flex; } .flex-table p > span { flex-shrink: 0; padding: 2px 4px; margin: 0; } .flex-table .num { font-family: var(--font-family-monospaced); color: var(--color-text); text-align: right; } .flex-table .number { white-space: nowrap; } .flex-table .head p > span { padding: 3px 4px; color: var(--color-table-header-text); background-color: var(--color-table-header-background); } .flex-table .head .num { font-family: var(--font-family); color: var(--color-table-header-text); background-color: var(--color-table-header-background); } .flex-table .totals p > span { color: var(--color-table-header-text); background-color: var(--color-table-header-background); } .journal p, .journal dl { border-bottom: thin solid var(--color-table-border); } .journal .payee { cursor: pointer; } .journal .postings { font-size: 0.9em; background-color: var(--color-journal-postings); opacity: 0.8; } .journal .postings .num { overflow: hidden; line-height: 16px; } .journal > li, .journal.show-custom .custom.budget, .journal.show-document .document.discovered, .journal.show-document .document.linked, .journal .metadata, .journal .postings { display: none; } .journal .head, .journal.show-balance .balance, .journal.show-close .close, .journal.show-custom .custom, .journal.show-document .document, .journal.show-note .note, .journal.show-open .open, .journal.show-pad .pad, .journal.show-query .query, .journal.show-metadata .metadata, .journal.show-postings .postings, .transaction.show-postings .postings, .transaction.show-postings .metadata { display: block; } .journal.show-transaction.show-cleared .transaction.cleared, .journal.show-transaction.show-pending .transaction.pending, .journal.show-transaction.show-other .transaction.other, .journal.show-document.show-discovered .document.discovered, .journal.show-document.show-linked .document.linked, .journal.show-custom.show-budget .custom.budget { display: block; } /* Metadata */ .journal .metadata { padding: 2px 0; margin: 0; font-size: 0.9em; } .journal .metadata dt { display: inline-block; float: left; width: auto; min-width: 4rem; margin-left: 9rem; color: var(--color-journal-metadata); } .journal .metadata dd { margin-left: 15rem; cursor: pointer; } .journal p > .num { width: 9rem; border-left: 1px solid var(--color-table-border); } .journal .datecell, .journal .flag { text-align: center; background-color: var(--entry-type-color); } .journal .datecell { width: 5.5rem; white-space: nowrap; } .journal .flag { width: 3rem; } .journal .change { font-weight: 500; } .journal .description { display: flex; flex: 1; align-items: center; padding-left: 8px; } .journal .description .separator { width: 4px; height: 4px; padding: 2px; margin: 0 6px; background-color: var(--color-text-lighter); } .journal .description .account-link { margin-right: 0.5em; } .journal .description .num { margin: 0 5px; } .journal .tag, .journal .link { margin-left: 8px; font-size: 0.9em; cursor: pointer; } .journal .tag { color: var(--color-journal-tag); } .journal .link { color: var(--color-journal-link); } .journal .bal { background-color: var(--entry-type-color); } .journal a:hover { filter: brightness(80%); } .journal .filename, .journal .url { font-family: var(--font-family-monospaced); font-size: 0.9em; } .journal .document .filename { margin-left: 1em; } .journal .indicators { display: flex; flex-shrink: 3; flex-wrap: wrap; align-items: center; justify-content: flex-end; cursor: pointer; } .journal .indicators span { min-width: 6px; height: 6px; padding: 0; margin-right: 4px; background-color: var(--color-journal-posting-indicator); border-radius: 3px; } .journal .indicators .pending, .journal .indicators .other { background-color: var(--entry-type-color); } .journal .indicators .metadata-indicator { height: 16px; padding: 0 6px; font-size: 10px; line-height: 16px; color: var(--color-journal-metadata-indicator); text-transform: lowercase; border-radius: 20px; } /* Collapsible trees * * some of the shared styles are in `journal-table.css` */ .tree-table.fullwidth { display: block; max-width: 100%; overflow-x: auto; } .tree-table p { margin-top: -1px; } .tree-table p > span { margin-right: -1px; border: 1px solid var(--color-table-header-background); } .tree-table .account-cell { display: flex; flex: 1; align-items: center; min-width: 14em; max-width: 30em; } .tree-table .account-cell.depth-1 { min-width: 13em; max-width: 29em; margin-left: 1em; } .tree-table .account-cell.depth-2 { min-width: 12em; max-width: 28em; margin-left: 2em; } .tree-table .account-cell.depth-3 { min-width: 11em; max-width: 27em; margin-left: 3em; } .tree-table .account-cell.depth-4 { min-width: 10em; max-width: 26em; margin-left: 4em; } .tree-table .account-cell.depth-5 { min-width: 9em; max-width: 25em; margin-left: 5em; } .tree-table .account-cell.depth-6 { min-width: 8em; max-width: 24em; margin-left: 6em; } .tree-table .account-cell.depth-7 { min-width: 7em; max-width: 23em; margin-left: 7em; } .tree-table .account-cell.depth-8 { min-width: 6em; max-width: 22em; margin-left: 8em; } .tree-table .account-cell.depth-9 { min-width: 5em; max-width: 21em; margin-left: 9em; } .tree-table .account-cell a { margin-left: 1em; } .tree-table .has-children { cursor: pointer; } .tree-table .has-children::before { margin: 0 -10px 0 0; content: ""; border-top: 5px solid var(--color-treetable-expander); border-right: 5px solid transparent; border-left: 5px solid transparent; } .tree-table .num { width: 10em; } .tree-table .num a { display: block; color: inherit; } .tree-table .other { width: 13em; } .tree-table .other a { display: block; color: inherit; } .tree-table .balance-children { display: block; opacity: 0.7; } .tree-table .has-balance .balance { display: block; } .tree-table .has-balance .balance-children { display: none; } .tree-table .toggled ol { display: none; } .tree-table .toggled .balance { display: none; } .tree-table .toggled .balance-children { display: block; color: var(--color-text); } .tree-table .toggled .has-children::before { transform: rotate(270deg); } .tree-table .expand-all { margin-left: 15px; font-weight: normal; color: inherit; opacity: 0.5; } .tree-table .diff { margin-right: 3px; font-size: 0.9em; color: var(--color-budget-zero); white-space: nowrap; } .tree-table .diff.negative { color: var(--color-budget-negative); } .tree-table .diff.positive { color: var(--color-budget-positive); } /* For two or more operating currencies, set a slightly smaller size. */ .two-currencies { font-size: 0.9em; } .two-currencies .num { width: 8em; } .two-currencies .other { width: 11em; } /* BASICS */ .CodeMirror { /* Set height, width, borders, and global font properties here */ font-family: monospace; height: 300px; color: black; direction: ltr; } /* PADDING */ .CodeMirror-lines { padding: 4px 0; /* Vertical padding around content */ } .CodeMirror pre.CodeMirror-line, .CodeMirror pre.CodeMirror-line-like { padding: 0 4px; /* Horizontal padding of content */ } .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { background-color: white; /* The little square between H and V scrollbars */ } /* GUTTER */ .CodeMirror-gutters { border-right: 1px solid #ddd; background-color: #f7f7f7; white-space: nowrap; } .CodeMirror-linenumbers {} .CodeMirror-linenumber { padding: 0 3px 0 5px; min-width: 20px; text-align: right; color: #999; white-space: nowrap; } .CodeMirror-guttermarker { color: black; } .CodeMirror-guttermarker-subtle { color: #999; } /* CURSOR */ .CodeMirror-cursor { border-left: 1px solid black; border-right: none; width: 0; } /* Shown when moving in bi-directional text */ .CodeMirror div.CodeMirror-secondarycursor { border-left: 1px solid silver; } .cm-fat-cursor .CodeMirror-cursor { width: auto; border: 0 !important; background: #7e7; } .cm-fat-cursor div.CodeMirror-cursors { z-index: 1; } .cm-fat-cursor-mark { background-color: rgba(20, 255, 20, 0.5); -webkit-animation: blink 1.06s steps(1) infinite; -moz-animation: blink 1.06s steps(1) infinite; animation: blink 1.06s steps(1) infinite; } .cm-animate-fat-cursor { width: auto; border: 0; -webkit-animation: blink 1.06s steps(1) infinite; -moz-animation: blink 1.06s steps(1) infinite; animation: blink 1.06s steps(1) infinite; background-color: #7e7; } @-moz-keyframes blink { 0% {} 50% { background-color: transparent; } 100% {} } @-webkit-keyframes blink { 0% {} 50% { background-color: transparent; } 100% {} } @keyframes blink { 0% {} 50% { background-color: transparent; } 100% {} } /* Can style cursor different in overwrite (non-insert) mode */ .CodeMirror-overwrite .CodeMirror-cursor {} .cm-tab { display: inline-block; text-decoration: inherit; } .CodeMirror-rulers { position: absolute; left: 0; right: 0; top: -50px; bottom: 0; overflow: hidden; } .CodeMirror-ruler { border-left: 1px solid #ccc; top: 0; bottom: 0; position: absolute; } /* DEFAULT THEME */ .cm-s-default .cm-header {color: blue;} .cm-s-default .cm-quote {color: #090;} .cm-negative {color: #d44;} .cm-positive {color: #292;} .cm-header, .cm-strong {font-weight: bold;} .cm-em {font-style: italic;} .cm-link {text-decoration: underline;} .cm-strikethrough {text-decoration: line-through;} .cm-s-default .cm-keyword {color: #708;} .cm-s-default .cm-atom {color: #219;} .cm-s-default .cm-number {color: #164;} .cm-s-default .cm-def {color: #00f;} .cm-s-default .cm-variable, .cm-s-default .cm-punctuation, .cm-s-default .cm-property, .cm-s-default .cm-operator {} .cm-s-default .cm-variable-2 {color: #05a;} .cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} .cm-s-default .cm-comment {color: #a50;} .cm-s-default .cm-string {color: #a11;} .cm-s-default .cm-string-2 {color: #f50;} .cm-s-default .cm-meta {color: #555;} .cm-s-default .cm-qualifier {color: #555;} .cm-s-default .cm-builtin {color: #30a;} .cm-s-default .cm-bracket {color: #997;} .cm-s-default .cm-tag {color: #170;} .cm-s-default .cm-attribute {color: #00c;} .cm-s-default .cm-hr {color: #999;} .cm-s-default .cm-link {color: #00c;} .cm-s-default .cm-error {color: #f00;} .cm-invalidchar {color: #f00;} .CodeMirror-composing { border-bottom: 2px solid; } /* Default styles for common addons */ div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;} div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } .CodeMirror-activeline-background {background: #e8f2ff;} /* STOP */ /* The rest of this file contains styles related to the mechanics of the editor. You probably shouldn't touch them. */ .CodeMirror { position: relative; overflow: hidden; background: white; } .CodeMirror-scroll { overflow: scroll !important; /* Things will break if this is overridden */ /* 30px is the magic margin used to hide the element's real scrollbars */ /* See overflow: hidden in .CodeMirror */ margin-bottom: -30px; margin-right: -30px; padding-bottom: 30px; height: 100%; outline: none; /* Prevent dragging from highlighting the element */ position: relative; } .CodeMirror-sizer { position: relative; border-right: 30px solid transparent; } /* The fake, visible scrollbars. Used to force redraw during scrolling before actual scrolling happens, thus preventing shaking and flickering artifacts. */ .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { position: absolute; z-index: 6; display: none; } .CodeMirror-vscrollbar { right: 0; top: 0; overflow-x: hidden; overflow-y: scroll; } .CodeMirror-hscrollbar { bottom: 0; left: 0; overflow-y: hidden; overflow-x: scroll; } .CodeMirror-scrollbar-filler { right: 0; bottom: 0; } .CodeMirror-gutter-filler { left: 0; bottom: 0; } .CodeMirror-gutters { position: absolute; left: 0; top: 0; min-height: 100%; z-index: 3; } .CodeMirror-gutter { white-space: normal; height: 100%; display: inline-block; vertical-align: top; margin-bottom: -30px; } .CodeMirror-gutter-wrapper { position: absolute; z-index: 4; background: none !important; border: none !important; } .CodeMirror-gutter-background { position: absolute; top: 0; bottom: 0; z-index: 4; } .CodeMirror-gutter-elt { position: absolute; cursor: default; z-index: 4; } .CodeMirror-gutter-wrapper ::selection { background-color: transparent } .CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } .CodeMirror-lines { cursor: text; min-height: 1px; /* prevents collapsing before first draw */ } .CodeMirror pre.CodeMirror-line, .CodeMirror pre.CodeMirror-line-like { /* Reset some styles that the rest of the page might have set */ -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; border-width: 0; background: transparent; font-family: inherit; font-size: inherit; margin: 0; white-space: pre; word-wrap: normal; line-height: inherit; color: inherit; z-index: 2; position: relative; overflow: visible; -webkit-tap-highlight-color: transparent; -webkit-font-variant-ligatures: contextual; font-variant-ligatures: contextual; } .CodeMirror-wrap pre.CodeMirror-line, .CodeMirror-wrap pre.CodeMirror-line-like { word-wrap: break-word; white-space: pre-wrap; word-break: normal; } .CodeMirror-linebackground { position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: 0; } .CodeMirror-linewidget { position: relative; z-index: 2; padding: 0.1px; /* Force widget margins to stay inside of the container */ } .CodeMirror-widget {} .CodeMirror-rtl pre { direction: rtl; } .CodeMirror-code { outline: none; } /* Force content-box sizing for the elements where we expect it */ .CodeMirror-scroll, .CodeMirror-sizer, .CodeMirror-gutter, .CodeMirror-gutters, .CodeMirror-linenumber { -moz-box-sizing: content-box; box-sizing: content-box; } .CodeMirror-measure { position: absolute; width: 100%; height: 0; overflow: hidden; visibility: hidden; } .CodeMirror-cursor { position: absolute; pointer-events: none; } .CodeMirror-measure pre { position: static; } div.CodeMirror-cursors { visibility: hidden; position: relative; z-index: 3; } div.CodeMirror-dragcursors { visibility: visible; } .CodeMirror-focused div.CodeMirror-cursors { visibility: visible; } .CodeMirror-selected { background: #d9d9d9; } .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } .CodeMirror-crosshair { cursor: crosshair; } .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } .cm-searching { background-color: #ffa; background-color: rgba(255, 255, 0, .4); } /* Used to force a border model for a node */ .cm-force-border { padding-right: .1px; } @media print { /* Hide the cursor when printing */ .CodeMirror div.CodeMirror-cursors { visibility: hidden; } } /* See issue #2901 */ .cm-tab-wrap-hack:after { content: ''; } /* Help users use markselection to safely style text background */ span.CodeMirror-selectedtext { background: none; } .CodeMirror-dialog { position: absolute; left: 0; right: 0; background: inherit; z-index: 15; padding: .1em .8em; overflow: hidden; color: inherit; } .CodeMirror-dialog-top { border-bottom: 1px solid #eee; top: 0; } .CodeMirror-dialog-bottom { border-top: 1px solid #eee; bottom: 0; } .CodeMirror-dialog input { border: none; outline: none; background: transparent; width: 20em; color: inherit; font-family: monospace; } .CodeMirror-dialog button { font-size: 70%; } .CodeMirror-hints { position: absolute; z-index: 10; overflow: hidden; list-style: none; margin: 0; padding: 2px; -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); box-shadow: 2px 3px 5px rgba(0,0,0,.2); border-radius: 3px; border: 1px solid silver; background: white; font-size: 90%; font-family: monospace; max-height: 20em; overflow-y: auto; } .CodeMirror-hint { margin: 0; padding: 0 4px; border-radius: 2px; white-space: pre; color: black; cursor: pointer; } li.CodeMirror-hint-active { background: #08f; color: white; } .CodeMirror-foldmarker { color: blue; text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px; font-family: arial; line-height: .3; cursor: pointer; } .CodeMirror-foldgutter { width: .7em; } .CodeMirror-foldgutter-open, .CodeMirror-foldgutter-folded { cursor: pointer; } .CodeMirror-foldgutter-open:after { content: "\25BE"; } .CodeMirror-foldgutter-folded:after { content: "\25B8"; } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1581856590.0 fava-1.14/fava/static/gen/app.js0000644000175000001440000377231200000000000016734 0ustar00jakobusers00000000000000(function () { 'use strict'; /** * Data validation. * * These functions allow us to ensure that `unknown` data obtained from, e.g., * an API, is of a specified type. */ class ValidationError extends Error { } /** * Validate as unknown (noop). */ function unknown(json) { return json; } /** * Validate a string. */ function string(json) { if (typeof json === "string") { return json; } throw new ValidationError(`Expected a string, got '${json}' instead.`); } /** * Validate a boolean. */ function boolean(json) { if (typeof json === "boolean") { return json; } throw new ValidationError(`Expected a boolean, got '${json}' instead.`); } /** * Validate a number. */ function number(json) { if (typeof json === "number") { return json; } throw new ValidationError(`Expected a number, got '${json}' instead.`); } /** * Validate a date (from a string). */ function date(json) { if (typeof json === "string" || json instanceof Date) { return new Date(json); } throw new ValidationError(`Expected a date: ${json}`); } /** * Validate a value to be equal to a constant value. */ function constant(value) { return (json) => { if (json === value) { return json; } throw new ValidationError(`Expected a constant: ${json}`); }; } /** * Validate a value that is of one of two given types. */ function union(a, b) { return (json) => { for (const validator of [a, b]) { try { return validator(json); } catch (exc) { // pass } } throw new ValidationError(`Validating union failed`); }; } /** * Lazy validator to allow for recursive structures. */ function lazy(func) { return (json) => { return func()(json); }; } /** * Validator for an array of values. */ function array(validator) { return (json) => { if (Array.isArray(json)) { const result = []; json.forEach(element => { result.push(validator(element)); }); return result; } throw new ValidationError(`Expected an array: ${json}`); }; } /** * Validator for a tuple of fixed length. */ function tuple(decoders) { return (json) => { if (Array.isArray(json) && json.length === 2) { const result = []; for (let i = 0; i < decoders.length; i += 1) { result[i] = decoders[i](json[i]); } return result; } throw new ValidationError(`Expected a tuple: ${json}`); }; } const isJsonObject = (json) => typeof json === "object" && json !== null && !Array.isArray(json); /** * Validator for an object with some given properties. */ function object(validators) { return (json) => { if (isJsonObject(json)) { const obj = {}; // eslint-disable-next-line no-restricted-syntax for (const key in validators) { if (Object.prototype.hasOwnProperty.call(validators, key)) { obj[key] = validators[key](json[key]); } } return obj; } throw new ValidationError(); }; } /** * Validator for a dict-like structure. */ function record(decoder) { return (json) => { if (isJsonObject(json)) { const ret = {}; // eslint-disable-next-line no-restricted-syntax for (const key in json) { if (Object.prototype.hasOwnProperty.call(json, key)) { ret[key] = decoder(json[key]); } } return ret; } throw new ValidationError(); }; } function noop() { } function assign(tar, src) { // @ts-ignore for (const k in src) tar[k] = src[k]; return tar; } function is_promise(value) { return value && typeof value === 'object' && typeof value.then === 'function'; } function run(fn) { return fn(); } function blank_object() { return Object.create(null); } function run_all(fns) { fns.forEach(run); } function is_function(thing) { return typeof thing === 'function'; } function safe_not_equal(a, b) { return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function'); } function subscribe(store, ...callbacks) { if (store == null) { return noop; } const unsub = store.subscribe(...callbacks); return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub; } function get_store_value(store) { let value; subscribe(store, _ => value = _)(); return value; } function component_subscribe(component, store, callback) { component.$$.on_destroy.push(subscribe(store, callback)); } function create_slot(definition, ctx, $$scope, fn) { if (definition) { const slot_ctx = get_slot_context(definition, ctx, $$scope, fn); return definition[0](slot_ctx); } } function get_slot_context(definition, ctx, $$scope, fn) { return definition[1] && fn ? assign($$scope.ctx.slice(), definition[1](fn(ctx))) : $$scope.ctx; } function get_slot_changes(definition, $$scope, dirty, fn) { if (definition[2] && fn) { const lets = definition[2](fn(dirty)); if (typeof $$scope.dirty === 'object') { const merged = []; const len = Math.max($$scope.dirty.length, lets.length); for (let i = 0; i < len; i += 1) { merged[i] = $$scope.dirty[i] | lets[i]; } return merged; } return $$scope.dirty | lets; } return $$scope.dirty; } function null_to_empty(value) { return value == null ? '' : value; } function set_store_value(store, ret, value = ret) { store.set(value); return ret; } function append(target, node) { target.appendChild(node); } function insert(target, node, anchor) { target.insertBefore(node, anchor || null); } function detach(node) { node.parentNode.removeChild(node); } function destroy_each(iterations, detaching) { for (let i = 0; i < iterations.length; i += 1) { if (iterations[i]) iterations[i].d(detaching); } } function element(name) { return document.createElement(name); } function svg_element(name) { return document.createElementNS('http://www.w3.org/2000/svg', name); } function text(data) { return document.createTextNode(data); } function space() { return text(' '); } function empty() { return text(''); } function listen(node, event, handler, options) { node.addEventListener(event, handler, options); return () => node.removeEventListener(event, handler, options); } function prevent_default(fn) { return function (event) { event.preventDefault(); // @ts-ignore return fn.call(this, event); }; } function attr(node, attribute, value) { if (value == null) node.removeAttribute(attribute); else if (node.getAttribute(attribute) !== value) node.setAttribute(attribute, value); } function set_attributes(node, attributes) { // @ts-ignore const descriptors = Object.getOwnPropertyDescriptors(node.__proto__); for (const key in attributes) { if (attributes[key] == null) { node.removeAttribute(key); } else if (key === 'style') { node.style.cssText = attributes[key]; } else if (descriptors[key] && descriptors[key].set) { node[key] = attributes[key]; } else { attr(node, key, attributes[key]); } } } function children(element) { return Array.from(element.childNodes); } function set_data(text, data) { data = '' + data; if (text.data !== data) text.data = data; } function set_input_value(input, value) { if (value != null || input.value) { input.value = value; } } function set_style(node, key, value, important) { node.style.setProperty(key, value, important ? 'important' : ''); } function select_option(select, value) { for (let i = 0; i < select.options.length; i += 1) { const option = select.options[i]; if (option.__value === value) { option.selected = true; return; } } } function select_value(select) { const selected_option = select.querySelector(':checked') || select.options[0]; return selected_option && selected_option.__value; } function add_resize_listener(element, fn) { if (getComputedStyle(element).position === 'static') { element.style.position = 'relative'; } const object = document.createElement('object'); object.setAttribute('style', 'display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; pointer-events: none; z-index: -1;'); object.setAttribute('aria-hidden', 'true'); object.type = 'text/html'; object.tabIndex = -1; let win; object.onload = () => { win = object.contentDocument.defaultView; win.addEventListener('resize', fn); }; if (/Trident/.test(navigator.userAgent)) { element.appendChild(object); object.data = 'about:blank'; } else { object.data = 'about:blank'; element.appendChild(object); } return { cancel: () => { win && win.removeEventListener && win.removeEventListener('resize', fn); element.removeChild(object); } }; } function toggle_class(element, name, toggle) { element.classList[toggle ? 'add' : 'remove'](name); } function custom_event(type, detail) { const e = document.createEvent('CustomEvent'); e.initCustomEvent(type, false, false, detail); return e; } class HtmlTag { constructor(html, anchor = null) { this.e = element('div'); this.a = anchor; this.u(html); } m(target, anchor = null) { for (let i = 0; i < this.n.length; i += 1) { insert(target, this.n[i], anchor); } this.t = target; } u(html) { this.e.innerHTML = html; this.n = Array.from(this.e.childNodes); } p(html) { this.d(); this.u(html); this.m(this.t, this.a); } d() { this.n.forEach(detach); } } let current_component; function set_current_component(component) { current_component = component; } function get_current_component() { if (!current_component) throw new Error(`Function called outside component initialization`); return current_component; } function onMount(fn) { get_current_component().$$.on_mount.push(fn); } function afterUpdate(fn) { get_current_component().$$.after_update.push(fn); } function createEventDispatcher() { const component = get_current_component(); return (type, detail) => { const callbacks = component.$$.callbacks[type]; if (callbacks) { // TODO are there situations where events could be dispatched // in a server (non-DOM) environment? const event = custom_event(type, detail); callbacks.slice().forEach(fn => { fn.call(component, event); }); } }; } // TODO figure out if we still want to support // shorthand events, or if we want to implement // a real bubbling mechanism function bubble(component, event) { const callbacks = component.$$.callbacks[event.type]; if (callbacks) { callbacks.slice().forEach(fn => fn(event)); } } const dirty_components = []; const binding_callbacks = []; const render_callbacks = []; const flush_callbacks = []; const resolved_promise = Promise.resolve(); let update_scheduled = false; function schedule_update() { if (!update_scheduled) { update_scheduled = true; resolved_promise.then(flush); } } function tick() { schedule_update(); return resolved_promise; } function add_render_callback(fn) { render_callbacks.push(fn); } function add_flush_callback(fn) { flush_callbacks.push(fn); } let flushing = false; const seen_callbacks = new Set(); function flush() { if (flushing) return; flushing = true; do { // first, call beforeUpdate functions // and update components for (let i = 0; i < dirty_components.length; i += 1) { const component = dirty_components[i]; set_current_component(component); update(component.$$); } dirty_components.length = 0; while (binding_callbacks.length) binding_callbacks.pop()(); // then, once components are updated, call // afterUpdate functions. This may cause // subsequent updates... for (let i = 0; i < render_callbacks.length; i += 1) { const callback = render_callbacks[i]; if (!seen_callbacks.has(callback)) { // ...so guard against infinite loops seen_callbacks.add(callback); callback(); } } render_callbacks.length = 0; } while (dirty_components.length); while (flush_callbacks.length) { flush_callbacks.pop()(); } update_scheduled = false; flushing = false; seen_callbacks.clear(); } function update($$) { if ($$.fragment !== null) { $$.update(); run_all($$.before_update); const dirty = $$.dirty; $$.dirty = [-1]; $$.fragment && $$.fragment.p($$.ctx, dirty); $$.after_update.forEach(add_render_callback); } } const outroing = new Set(); let outros; function group_outros() { outros = { r: 0, c: [], p: outros // parent group }; } function check_outros() { if (!outros.r) { run_all(outros.c); } outros = outros.p; } function transition_in(block, local) { if (block && block.i) { outroing.delete(block); block.i(local); } } function transition_out(block, local, detach, callback) { if (block && block.o) { if (outroing.has(block)) return; outroing.add(block); outros.c.push(() => { outroing.delete(block); if (callback) { if (detach) block.d(1); callback(); } }); block.o(local); } } function handle_promise(promise, info) { const token = info.token = {}; function update(type, index, key, value) { if (info.token !== token) return; info.resolved = value; let child_ctx = info.ctx; if (key !== undefined) { child_ctx = child_ctx.slice(); child_ctx[key] = value; } const block = type && (info.current = type)(child_ctx); let needs_flush = false; if (info.block) { if (info.blocks) { info.blocks.forEach((block, i) => { if (i !== index && block) { group_outros(); transition_out(block, 1, 1, () => { info.blocks[i] = null; }); check_outros(); } }); } else { info.block.d(1); } block.c(); transition_in(block, 1); block.m(info.mount(), info.anchor); needs_flush = true; } info.block = block; if (info.blocks) info.blocks[index] = block; if (needs_flush) { flush(); } } if (is_promise(promise)) { const current_component = get_current_component(); promise.then(value => { set_current_component(current_component); update(info.then, 1, info.value, value); set_current_component(null); }, error => { set_current_component(current_component); update(info.catch, 2, info.error, error); set_current_component(null); }); // if we previously had a then/catch block, destroy it if (info.current !== info.pending) { update(info.pending, 0); return true; } } else { if (info.current !== info.then) { update(info.then, 1, info.value, promise); return true; } info.resolved = promise; } } function outro_and_destroy_block(block, lookup) { transition_out(block, 1, 1, () => { lookup.delete(block.key); }); } function update_keyed_each(old_blocks, dirty, get_key, dynamic, ctx, list, lookup, node, destroy, create_each_block, next, get_context) { let o = old_blocks.length; let n = list.length; let i = o; const old_indexes = {}; while (i--) old_indexes[old_blocks[i].key] = i; const new_blocks = []; const new_lookup = new Map(); const deltas = new Map(); i = n; while (i--) { const child_ctx = get_context(ctx, list, i); const key = get_key(child_ctx); let block = lookup.get(key); if (!block) { block = create_each_block(key, child_ctx); block.c(); } else if (dynamic) { block.p(child_ctx, dirty); } new_lookup.set(key, new_blocks[i] = block); if (key in old_indexes) deltas.set(key, Math.abs(i - old_indexes[key])); } const will_move = new Set(); const did_move = new Set(); function insert(block) { transition_in(block, 1); block.m(node, next); lookup.set(block.key, block); next = block.first; n--; } while (o && n) { const new_block = new_blocks[n - 1]; const old_block = old_blocks[o - 1]; const new_key = new_block.key; const old_key = old_block.key; if (new_block === old_block) { // do nothing next = new_block.first; o--; n--; } else if (!new_lookup.has(old_key)) { // remove old block destroy(old_block, lookup); o--; } else if (!lookup.has(new_key) || will_move.has(new_key)) { insert(new_block); } else if (did_move.has(old_key)) { o--; } else if (deltas.get(new_key) > deltas.get(old_key)) { did_move.add(new_key); insert(new_block); } else { will_move.add(old_key); o--; } } while (o--) { const old_block = old_blocks[o]; if (!new_lookup.has(old_block.key)) destroy(old_block, lookup); } while (n) insert(new_blocks[n - 1]); return new_blocks; } function get_spread_update(levels, updates) { const update = {}; const to_null_out = {}; const accounted_for = { $$scope: 1 }; let i = levels.length; while (i--) { const o = levels[i]; const n = updates[i]; if (n) { for (const key in o) { if (!(key in n)) to_null_out[key] = 1; } for (const key in n) { if (!accounted_for[key]) { update[key] = n[key]; accounted_for[key] = 1; } } levels[i] = n; } else { for (const key in o) { accounted_for[key] = 1; } } } for (const key in to_null_out) { if (!(key in update)) update[key] = undefined; } return update; } function get_spread_object(spread_props) { return typeof spread_props === 'object' && spread_props !== null ? spread_props : {}; } function bind(component, name, callback) { const index = component.$$.props[name]; if (index !== undefined) { component.$$.bound[index] = callback; callback(component.$$.ctx[index]); } } function create_component(block) { block && block.c(); } function mount_component(component, target, anchor) { const { fragment, on_mount, on_destroy, after_update } = component.$$; fragment && fragment.m(target, anchor); // onMount happens before the initial afterUpdate add_render_callback(() => { const new_on_destroy = on_mount.map(run).filter(is_function); if (on_destroy) { on_destroy.push(...new_on_destroy); } else { // Edge case - component was destroyed immediately, // most likely as a result of a binding initialising run_all(new_on_destroy); } component.$$.on_mount = []; }); after_update.forEach(add_render_callback); } function destroy_component(component, detaching) { const $$ = component.$$; if ($$.fragment !== null) { run_all($$.on_destroy); $$.fragment && $$.fragment.d(detaching); // TODO null out other refs, including component.$$ (but need to // preserve final state?) $$.on_destroy = $$.fragment = null; $$.ctx = []; } } function make_dirty(component, i) { if (component.$$.dirty[0] === -1) { dirty_components.push(component); schedule_update(); component.$$.dirty.fill(0); } component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31)); } function init(component, options, instance, create_fragment, not_equal, props, dirty = [-1]) { const parent_component = current_component; set_current_component(component); const prop_values = options.props || {}; const $$ = component.$$ = { fragment: null, ctx: null, // state props, update: noop, not_equal, bound: blank_object(), // lifecycle on_mount: [], on_destroy: [], before_update: [], after_update: [], context: new Map(parent_component ? parent_component.$$.context : []), // everything else callbacks: blank_object(), dirty }; let ready = false; $$.ctx = instance ? instance(component, prop_values, (i, ret, ...rest) => { const value = rest.length ? rest[0] : ret; if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) { if ($$.bound[i]) $$.bound[i](value); if (ready) make_dirty(component, i); } return ret; }) : []; $$.update(); ready = true; run_all($$.before_update); // `false` as a special case of no DOM component $$.fragment = create_fragment ? create_fragment($$.ctx) : false; if (options.target) { if (options.hydrate) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion $$.fragment && $$.fragment.l(children(options.target)); } else { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion $$.fragment && $$.fragment.c(); } if (options.intro) transition_in(component.$$.fragment); mount_component(component, options.target, options.anchor); flush(); } set_current_component(parent_component); } class SvelteComponent { $destroy() { destroy_component(this, 1); this.$destroy = noop; } $on(type, callback) { const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = [])); callbacks.push(callback); return () => { const index = callbacks.indexOf(callback); if (index !== -1) callbacks.splice(index, 1); }; } $set() { // overridden by instance, if it has props } } const subscriber_queue = []; /** * Creates a `Readable` store that allows reading by subscription. * @param value initial value * @param {StartStopNotifier}start start and stop notifications for subscriptions */ function readable(value, start) { return { subscribe: writable(value, start).subscribe, }; } /** * Create a `Writable` store that allows both updating and reading by subscription. * @param {*=}value initial value * @param {StartStopNotifier=}start start and stop notifications for subscriptions */ function writable(value, start = noop) { let stop; const subscribers = []; function set(new_value) { if (safe_not_equal(value, new_value)) { value = new_value; if (stop) { // store is ready const run_queue = !subscriber_queue.length; for (let i = 0; i < subscribers.length; i += 1) { const s = subscribers[i]; s[1](); subscriber_queue.push(s, value); } if (run_queue) { for (let i = 0; i < subscriber_queue.length; i += 2) { subscriber_queue[i][0](subscriber_queue[i + 1]); } subscriber_queue.length = 0; } } } } function update(fn) { set(fn(value)); } function subscribe(run, invalidate = noop) { const subscriber = [run, invalidate]; subscribers.push(subscriber); if (subscribers.length === 1) { stop = start(set) || noop; } run(value); return () => { const index = subscribers.indexOf(subscriber); if (index !== -1) { subscribers.splice(index, 1); } if (subscribers.length === 0) { stop(); stop = null; } }; } return { set, update, subscribe }; } function derived(stores, fn, initial_value) { const single = !Array.isArray(stores); const stores_array = single ? [stores] : stores; const auto = fn.length < 2; return readable(initial_value, (set) => { let inited = false; const values = []; let pending = 0; let cleanup = noop; const sync = () => { if (pending) { return; } cleanup(); const result = fn(single ? values[0] : values, set); if (auto) { set(result); } else { cleanup = is_function(result) ? result : noop; } }; const unsubscribers = stores_array.map((store, i) => subscribe(store, (value) => { values[i] = value; pending &= ~(1 << i); if (inited) { sync(); } }, () => { pending |= (1 << i); })); inited = true; sync(); return function stop() { run_all(unsubscribers); cleanup(); }; }); } const urlHash = writable(""); const conversion = writable(""); const interval = writable("month"); const favaAPIValidator = object({ accountURL: string, accounts: array(string), baseURL: string, currencies: array(string), documentTitle: string, errors: number, favaOptions: object({ "auto-reload": boolean, "currency-column": number, conversion: string, interval: string, locale: union(string, constant(null)), }), have_excel: boolean, incognito: boolean, links: array(string), options: object({ commodities: array(string), documents: array(string), operating_currency: array(string), }), pageTitle: string, payees: array(string), tags: array(string), years: array(number), }); const favaAPI = { accountURL: "", accounts: [], baseURL: "", currencies: [], documentTitle: "", errors: 0, favaOptions: { "auto-reload": false, "currency-column": 80, conversion: "at_cost", interval: "month", locale: null, }, have_excel: false, incognito: false, links: [], pageTitle: "", payees: [], options: { commodities: [], documents: [], operating_currency: [], }, tags: [], years: [], }; const favaAPIStore = writable(favaAPI); favaAPIStore.subscribe(val => { Object.assign(favaAPI, val); }); const filters = writable({ time: "", filter: "", account: "", }); const urlSyncedParams = [ "account", "charts", "conversion", "filter", "interval", "time", ]; function closeOverlay() { if (window.location.hash) { window.history.pushState({}, "", "#"); } urlHash.set(""); } /** * Select a single element. */ function select(expr, con = document) { return con.querySelector(expr); } /** * Select multiple elements (and convert NodeList to Array). */ function selectAll(expr, con = document) { return Array.from(con.querySelectorAll(expr)); } function getScriptTagJSON(selector) { const el = select(selector); if (!el) { return null; } return JSON.parse(el.innerHTML); } let translations; /** * Translate the given string. */ function _(text) { if (translations === undefined) { translations = record(string)(getScriptTagJSON("#translations")); } return translations[text] || text; } /** * Execute the callback of the event of given type is fired on something * matching selector. */ function delegate(element, type, selector, callback) { if (!element) { return; } element.addEventListener(type, event => { let { target } = event; if (!target || !(target instanceof Node)) { return; } if (!(target instanceof Element)) { target = target.parentNode; } if (target instanceof Element) { const closest = target.closest(selector); if (closest) { callback(event, closest); } } }); } /** * Bind an event to element, only run the callback once. */ function once(element, event, callback) { function runOnce(ev) { element.removeEventListener(event, runOnce); callback.apply(element, [ev]); } element.addEventListener(event, runOnce); } function ready() { return new Promise(resolve => { if (document.readyState !== "loading") { resolve(); } else { document.addEventListener("DOMContentLoaded", resolve); } }); } /** * Handles JSON content for a Promise returned by fetch, also handling an HTTP * error status. */ function handleJSON(response) { if (!response.ok) { return Promise.reject(response.statusText); } return response.json().then(data => { if (!data.success) { return Promise.reject(data.error); } return data; }); } /** * Handles text content for a Promise returned by fetch, also handling an HTTP * error status. */ function handleText(response) { if (!response.ok) { return Promise.reject(response.statusText); } return response.text(); } function fetch(input, init = {}) { const defaults = { credentials: "same-origin", }; return window.fetch(input, Object.assign(defaults, init)); } const validateAPIResponse = object({ data: unknown }); /** * Get the URL string for one of Fava's reports. */ function urlFor(report, params) { let url = `${favaAPI.baseURL}${report}`; if (params) { const urlParams = new URLSearchParams(); Object.entries(params).forEach(([key, value]) => { urlParams.set(key, value); }); url += `?${urlParams.toString()}`; } return url; } /** * Fetch an API endpoint and convert the JSON data to an object. * @param endpoint - the endpoint to fetch * @param params - a string to append as params or an object. */ async function fetchAPI(endpoint, params) { const url = urlFor(`api/${endpoint}`, params); const responseData = await fetch(url); const json = await handleJSON(responseData); return validateAPIResponse(json).data; } const putAPIValidators = { add_entries: string, format_source: string, source: string, }; /** * Fetch an API endpoint and convert the JSON data to an object. * @param endpoint - the endpoint to fetch * @param params - a string to append as params or an object. */ async function putAPI(endpoint, body) { const res = await fetch(`${favaAPI.baseURL}api/${endpoint}/`, { method: "PUT", headers: { "Content-Type": "application/json", }, body: JSON.stringify(body), }).then(handleJSON); const { data } = validateAPIResponse(res); return putAPIValidators[endpoint](data); } /** * Fuzzy match a pattern against a string. * * Returns true if all characters of `pattern` can be found in order in * `string`. For lowercase characters in `pattern` match both lower and upper * case, for uppercase only an exact match counts. */ function fuzzytest(pattern, text) { let pindex = 0; for (let index = 0; index < text.length; index += 1) { const char = text[index]; const search = pattern[pindex]; if (char === search || char.toLowerCase() === search) { pindex += 1; } } return pindex === pattern.length; } /** * Wrap fuzzy matched characters. * * Wrap all occurences of characters of `pattern` (in order) in `string` in * tags. */ function fuzzywrap(pattern, text) { let pindex = 0; const result = []; for (let index = 0; index < text.length; index += 1) { const char = text[index]; const search = pattern[pindex]; if (char === search || char.toLowerCase() === search) { result.push(`${char}`); pindex += 1; } else { result.push(char); } } return result.join(""); } /** * Minimal event handler */ class Events { constructor() { this.events = {}; } on(event, callback) { this.events[event] = this.events[event] || []; this.events[event].push(callback); } once(event, callback) { const runOnce = (arg) => { this.remove(event, runOnce); callback(arg); }; this.on(event, runOnce); } remove(event, callback) { if (!this.events[event].length) { return; } this.events[event] = this.events[event].filter(c => c !== callback); } trigger(event, arg) { if (!this.events[event]) { return; } this.events[event].forEach(callback => { callback(arg); }); } } // This global event handler is used by separate parts of the UI to // communicate. const e = new Events(); /* * Show a notification containing the given `msg` text and having class `cls`. * The notification is automatically removed after 5 seconds and on click * `callback` is called. * * @param {string} msg - The message to diplay * @param {string} cls - The message type. * @param {function} callback - The callback to execute on click.. */ function notify(msg, cls = "info", callback) { const notification = document.createElement("li"); notification.classList.add(cls); notification.appendChild(document.createTextNode(msg)); const notificationList = select("#notifications"); if (!notificationList) { throw new Error(); } notificationList.append(notification); notification.addEventListener("click", () => { notification.remove(); if (callback) { callback(); } }); setTimeout(() => { notification.remove(); }, 5000); } delegate(select("#notifications"), "click", "li", (event, closest) => { closest.remove(); }); const showCharts = writable(true); const activeChart = writable({}); const chartMode = writable("treemap"); const chartCurrency = writable(""); const conversions = derived(favaAPIStore, favaAPI => [ ["at_cost", _("At Cost")], ["at_value", _("At Market Value")], ["units", _("Units")], ...favaAPI.options.operating_currency .sort() .map(currency => [currency, `Converted to ${currency}`]), ...favaAPI.options.commodities .sort() .filter(c => !favaAPI.options.operating_currency.includes(c) && c.length <= 3) .map(currency => [currency, `Converted to ${currency}`]), ]); // TODO _('Converted to %(currency)s', currency=currency) // Routing class Router { constructor() { this.state = { hash: window.location.hash, pathname: window.location.pathname, search: window.location.search, }; } // This should be called once when the page has been loaded. Initializes the // router and takes over clicking on links. init() { urlHash.set(window.location.hash.slice(1)); this.updateState(); window.addEventListener("popstate", () => { urlHash.set(window.location.hash.slice(1)); if (window.location.hash !== this.state.hash && window.location.pathname === this.state.pathname && window.location.search === this.state.search) { this.updateState(); } else if (window.location.pathname !== this.state.pathname || window.location.search !== this.state.search) { this.loadURL(window.location.href, false); } }); this.takeOverLinks(); } // Go to URL. If load is `true`, load the page at URL, otherwise only update // the current state. navigate(url, load = true) { if (load) { this.loadURL(url); } else { window.history.pushState(null, "", url); this.updateState(); } } /* * Replace
contents with the page at `url`. * * If `historyState` is false, do not create a history state and do not * scroll to top. */ async loadURL(url, historyState = true) { const state = { interrupt: false }; e.trigger("navigate", state); if (state.interrupt) { return; } const getUrl = new URL(url); getUrl.searchParams.set("partial", "true"); const svg = select(".fava-icon"); if (svg) { svg.classList.add("loading"); } try { const content = await fetch(getUrl.toString()).then(handleText); if (historyState) { window.history.pushState(null, "", url); window.scroll(0, 0); } this.updateState(); const article = select("article"); if (article) { article.innerHTML = content; } e.trigger("page-loaded"); urlHash.set(window.location.hash.slice(1)); } catch (error) { notify(`Loading ${url} failed.`, "error"); } finally { if (svg) { svg.classList.remove("loading"); } } } /* * Update the routers state object. * * The state object is used to distinguish between the user navigating the * browser history or the hash changing. */ updateState() { this.state = { hash: window.location.hash, pathname: window.location.pathname, search: window.location.search, }; } /* * Intercept all clicks on links () and .navigate() to the link instead. * * Doesn't intercept if * - a button different from the main button is used, * - a modifier key is pressed, * - the link starts with a hash '#', or * - the link has a `data-remote` attribute. */ takeOverLinks() { delegate(document, "click", "a", (event, link) => { if ((link.getAttribute("href") || "").charAt(0) === "#" || link.host !== window.location.host || link.hasAttribute("data-remote") || link.protocol.indexOf("http") !== 0 || event.defaultPrevented) { return; } // update sidebar links if (link.closest("aside")) { const newURL = new URL(link.href); const oldParams = new URL(window.location.href).searchParams; for (const name of urlSyncedParams) { const value = oldParams.get(name); if (value) { newURL.searchParams.set(name, value); } else { newURL.searchParams.delete(name); } } link.href = newURL.toString(); } if (event.button !== 0 || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) { return; } event.preventDefault(); this.navigate(link.href); }); } /* * Reload the page. */ reload() { this.loadURL(window.location.href, false); } } const router = new Router(); e.on("page-init", () => { select("#reload-page").addEventListener("click", () => { router.reload(); }); const params = new URL(window.location.href).searchParams; filters.set({ time: params.get("time") || "", filter: params.get("filter") || "", account: params.get("account") || "", }); filters.subscribe(fs => { const newURL = new URL(window.location.href); for (const name of Object.keys(fs)) { const value = fs[name]; if (value) { newURL.searchParams.set(name, value); } else { newURL.searchParams.delete(name); } } const url = newURL.toString(); if (url !== window.location.href) { router.navigate(url); } }); function syncStoreValueToUrl(store, name, defaultValue, shouldLoad) { let value; if (typeof defaultValue === "boolean") { value = (params.get(name) !== "false" && defaultValue); } else { value = params.get(name) || defaultValue; } store.set(value); store.subscribe((val) => { const newURL = new URL(window.location.href); newURL.searchParams.set(name, val.toString()); if (val === defaultValue) { newURL.searchParams.delete(name); } const url = newURL.toString(); if (url !== window.location.href) { router.navigate(url, shouldLoad); } }); } // Set initial values from URL and update URL on store changes syncStoreValueToUrl(interval, "interval", favaAPI.favaOptions.interval, true); syncStoreValueToUrl(conversion, "conversion", favaAPI.favaOptions.conversion, true); syncStoreValueToUrl(showCharts, "charts", true, false); }); function ascending(a, b) { return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; } function bisector(compare) { if (compare.length === 1) compare = ascendingComparator(compare); return { left: function(a, x, lo, hi) { if (lo == null) lo = 0; if (hi == null) hi = a.length; while (lo < hi) { var mid = lo + hi >>> 1; if (compare(a[mid], x) < 0) lo = mid + 1; else hi = mid; } return lo; }, right: function(a, x, lo, hi) { if (lo == null) lo = 0; if (hi == null) hi = a.length; while (lo < hi) { var mid = lo + hi >>> 1; if (compare(a[mid], x) > 0) hi = mid; else lo = mid + 1; } return lo; } }; } function ascendingComparator(f) { return function(d, x) { return ascending(f(d), x); }; } var ascendingBisect = bisector(ascending); var bisectRight = ascendingBisect.right; function extent(values, valueof) { let min; let max; if (valueof === undefined) { for (const value of values) { if (value != null) { if (min === undefined) { if (value >= value) min = max = value; } else { if (min > value) min = value; if (max < value) max = value; } } } } else { let index = -1; for (let value of values) { if ((value = valueof(value, ++index, values)) != null) { if (min === undefined) { if (value >= value) min = max = value; } else { if (min > value) min = value; if (max < value) max = value; } } } } return [min, max]; } function identity(x) { return x; } function group(values, ...keys) { return nest(values, identity, identity, keys); } function nest(values, map, reduce, keys) { return (function regroup(values, i) { if (i >= keys.length) return reduce(values); const groups = new Map(); const keyof = keys[i++]; let index = -1; for (const value of values) { const key = keyof(value, ++index, values); const group = groups.get(key); if (group) group.push(value); else groups.set(key, [value]); } for (const [key, values] of groups) { groups.set(key, regroup(values, i)); } return map(groups); })(values, 0); } function sequence(start, stop, step) { start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step; var i = -1, n = Math.max(0, Math.ceil((stop - start) / step)) | 0, range = new Array(n); while (++i < n) { range[i] = start + i * step; } return range; } var e10 = Math.sqrt(50), e5 = Math.sqrt(10), e2 = Math.sqrt(2); function ticks(start, stop, count) { var reverse, i = -1, n, ticks, step; stop = +stop, start = +start, count = +count; if (start === stop && count > 0) return [start]; if (reverse = stop < start) n = start, start = stop, stop = n; if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return []; if (step > 0) { start = Math.ceil(start / step); stop = Math.floor(stop / step); ticks = new Array(n = Math.ceil(stop - start + 1)); while (++i < n) ticks[i] = (start + i) * step; } else { start = Math.floor(start * step); stop = Math.ceil(stop * step); ticks = new Array(n = Math.ceil(start - stop + 1)); while (++i < n) ticks[i] = (start - i) / step; } if (reverse) ticks.reverse(); return ticks; } function tickIncrement(start, stop, count) { var step = (stop - start) / Math.max(0, count), power = Math.floor(Math.log(step) / Math.LN10), error = step / Math.pow(10, power); return power >= 0 ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power) : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1); } function tickStep(start, stop, count) { var step0 = Math.abs(stop - start) / Math.max(0, count), step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)), error = step0 / step1; if (error >= e10) step1 *= 10; else if (error >= e5) step1 *= 5; else if (error >= e2) step1 *= 2; return stop < start ? -step1 : step1; } function max(values, valueof) { let max; if (valueof === undefined) { for (const value of values) { if (value != null && (max < value || (max === undefined && value >= value))) { max = value; } } } else { let index = -1; for (let value of values) { if ((value = valueof(value, ++index, values)) != null && (max < value || (max === undefined && value >= value))) { max = value; } } } return max; } function min(values, valueof) { let min; if (valueof === undefined) { for (const value of values) { if (value != null && (min > value || (min === undefined && value >= value))) { min = value; } } } else { let index = -1; for (let value of values) { if ((value = valueof(value, ++index, values)) != null && (min > value || (min === undefined && value >= value))) { min = value; } } } return min; } function* flatten(arrays) { for (const array of arrays) { yield* array; } } function merge(arrays) { return Array.from(flatten(arrays)); } function count(node) { var sum = 0, children = node.children, i = children && children.length; if (!i) sum = 1; else while (--i >= 0) sum += children[i].value; node.value = sum; } function node_count() { return this.eachAfter(count); } function node_each(callback) { var node = this, current, next = [node], children, i, n; do { current = next.reverse(), next = []; while (node = current.pop()) { callback(node), children = node.children; if (children) for (i = 0, n = children.length; i < n; ++i) { next.push(children[i]); } } } while (next.length); return this; } function node_eachBefore(callback) { var node = this, nodes = [node], children, i; while (node = nodes.pop()) { callback(node), children = node.children; if (children) for (i = children.length - 1; i >= 0; --i) { nodes.push(children[i]); } } return this; } function node_eachAfter(callback) { var node = this, nodes = [node], next = [], children, i, n; while (node = nodes.pop()) { next.push(node), children = node.children; if (children) for (i = 0, n = children.length; i < n; ++i) { nodes.push(children[i]); } } while (node = next.pop()) { callback(node); } return this; } function node_sum(value) { return this.eachAfter(function(node) { var sum = +value(node.data) || 0, children = node.children, i = children && children.length; while (--i >= 0) sum += children[i].value; node.value = sum; }); } function node_sort(compare) { return this.eachBefore(function(node) { if (node.children) { node.children.sort(compare); } }); } function node_path(end) { var start = this, ancestor = leastCommonAncestor(start, end), nodes = [start]; while (start !== ancestor) { start = start.parent; nodes.push(start); } var k = nodes.length; while (end !== ancestor) { nodes.splice(k, 0, end); end = end.parent; } return nodes; } function leastCommonAncestor(a, b) { if (a === b) return a; var aNodes = a.ancestors(), bNodes = b.ancestors(), c = null; a = aNodes.pop(); b = bNodes.pop(); while (a === b) { c = a; a = aNodes.pop(); b = bNodes.pop(); } return c; } function node_ancestors() { var node = this, nodes = [node]; while (node = node.parent) { nodes.push(node); } return nodes; } function node_descendants() { var nodes = []; this.each(function(node) { nodes.push(node); }); return nodes; } function node_leaves() { var leaves = []; this.eachBefore(function(node) { if (!node.children) { leaves.push(node); } }); return leaves; } function node_links() { var root = this, links = []; root.each(function(node) { if (node !== root) { // Don’t include the root’s parent, if any. links.push({source: node.parent, target: node}); } }); return links; } function hierarchy(data, children) { var root = new Node$1(data), valued = +data.value && (root.value = data.value), node, nodes = [root], child, childs, i, n; if (children == null) children = defaultChildren; while (node = nodes.pop()) { if (valued) node.value = +node.data.value; if ((childs = children(node.data)) && (n = childs.length)) { node.children = new Array(n); for (i = n - 1; i >= 0; --i) { nodes.push(child = node.children[i] = new Node$1(childs[i])); child.parent = node; child.depth = node.depth + 1; } } } return root.eachBefore(computeHeight); } function node_copy() { return hierarchy(this).eachBefore(copyData); } function defaultChildren(d) { return d.children; } function copyData(node) { node.data = node.data.data; } function computeHeight(node) { var height = 0; do node.height = height; while ((node = node.parent) && (node.height < ++height)); } function Node$1(data) { this.data = data; this.depth = this.height = 0; this.parent = null; } Node$1.prototype = hierarchy.prototype = { constructor: Node$1, count: node_count, each: node_each, eachAfter: node_eachAfter, eachBefore: node_eachBefore, sum: node_sum, sort: node_sort, path: node_path, ancestors: node_ancestors, descendants: node_descendants, leaves: node_leaves, links: node_links, copy: node_copy }; function required(f) { if (typeof f !== "function") throw new Error; return f; } function constantZero() { return 0; } function constant$1(x) { return function() { return x; }; } function roundNode(node) { node.x0 = Math.round(node.x0); node.y0 = Math.round(node.y0); node.x1 = Math.round(node.x1); node.y1 = Math.round(node.y1); } function treemapDice(parent, x0, y0, x1, y1) { var nodes = parent.children, node, i = -1, n = nodes.length, k = parent.value && (x1 - x0) / parent.value; while (++i < n) { node = nodes[i], node.y0 = y0, node.y1 = y1; node.x0 = x0, node.x1 = x0 += node.value * k; } } function partition() { var dx = 1, dy = 1, padding = 0, round = false; function partition(root) { var n = root.height + 1; root.x0 = root.y0 = padding; root.x1 = dx; root.y1 = dy / n; root.eachBefore(positionNode(dy, n)); if (round) root.eachBefore(roundNode); return root; } function positionNode(dy, n) { return function(node) { if (node.children) { treemapDice(node, node.x0, dy * (node.depth + 1) / n, node.x1, dy * (node.depth + 2) / n); } var x0 = node.x0, y0 = node.y0, x1 = node.x1 - padding, y1 = node.y1 - padding; if (x1 < x0) x0 = x1 = (x0 + x1) / 2; if (y1 < y0) y0 = y1 = (y0 + y1) / 2; node.x0 = x0; node.y0 = y0; node.x1 = x1; node.y1 = y1; }; } partition.round = function(x) { return arguments.length ? (round = !!x, partition) : round; }; partition.size = function(x) { return arguments.length ? (dx = +x[0], dy = +x[1], partition) : [dx, dy]; }; partition.padding = function(x) { return arguments.length ? (padding = +x, partition) : padding; }; return partition; } function treemapSlice(parent, x0, y0, x1, y1) { var nodes = parent.children, node, i = -1, n = nodes.length, k = parent.value && (y1 - y0) / parent.value; while (++i < n) { node = nodes[i], node.x0 = x0, node.x1 = x1; node.y0 = y0, node.y1 = y0 += node.value * k; } } var phi = (1 + Math.sqrt(5)) / 2; function squarifyRatio(ratio, parent, x0, y0, x1, y1) { var rows = [], nodes = parent.children, row, nodeValue, i0 = 0, i1 = 0, n = nodes.length, dx, dy, value = parent.value, sumValue, minValue, maxValue, newRatio, minRatio, alpha, beta; while (i0 < n) { dx = x1 - x0, dy = y1 - y0; // Find the next non-empty node. do sumValue = nodes[i1++].value; while (!sumValue && i1 < n); minValue = maxValue = sumValue; alpha = Math.max(dy / dx, dx / dy) / (value * ratio); beta = sumValue * sumValue * alpha; minRatio = Math.max(maxValue / beta, beta / minValue); // Keep adding nodes while the aspect ratio maintains or improves. for (; i1 < n; ++i1) { sumValue += nodeValue = nodes[i1].value; if (nodeValue < minValue) minValue = nodeValue; if (nodeValue > maxValue) maxValue = nodeValue; beta = sumValue * sumValue * alpha; newRatio = Math.max(maxValue / beta, beta / minValue); if (newRatio > minRatio) { sumValue -= nodeValue; break; } minRatio = newRatio; } // Position and record the row orientation. rows.push(row = {value: sumValue, dice: dx < dy, children: nodes.slice(i0, i1)}); if (row.dice) treemapDice(row, x0, y0, x1, value ? y0 += dy * sumValue / value : y1); else treemapSlice(row, x0, y0, value ? x0 += dx * sumValue / value : x1, y1); value -= sumValue, i0 = i1; } return rows; } var squarify = (function custom(ratio) { function squarify(parent, x0, y0, x1, y1) { squarifyRatio(ratio, parent, x0, y0, x1, y1); } squarify.ratio = function(x) { return custom((x = +x) > 1 ? x : 1); }; return squarify; })(phi); function treemap() { var tile = squarify, round = false, dx = 1, dy = 1, paddingStack = [0], paddingInner = constantZero, paddingTop = constantZero, paddingRight = constantZero, paddingBottom = constantZero, paddingLeft = constantZero; function treemap(root) { root.x0 = root.y0 = 0; root.x1 = dx; root.y1 = dy; root.eachBefore(positionNode); paddingStack = [0]; if (round) root.eachBefore(roundNode); return root; } function positionNode(node) { var p = paddingStack[node.depth], x0 = node.x0 + p, y0 = node.y0 + p, x1 = node.x1 - p, y1 = node.y1 - p; if (x1 < x0) x0 = x1 = (x0 + x1) / 2; if (y1 < y0) y0 = y1 = (y0 + y1) / 2; node.x0 = x0; node.y0 = y0; node.x1 = x1; node.y1 = y1; if (node.children) { p = paddingStack[node.depth + 1] = paddingInner(node) / 2; x0 += paddingLeft(node) - p; y0 += paddingTop(node) - p; x1 -= paddingRight(node) - p; y1 -= paddingBottom(node) - p; if (x1 < x0) x0 = x1 = (x0 + x1) / 2; if (y1 < y0) y0 = y1 = (y0 + y1) / 2; tile(node, x0, y0, x1, y1); } } treemap.round = function(x) { return arguments.length ? (round = !!x, treemap) : round; }; treemap.size = function(x) { return arguments.length ? (dx = +x[0], dy = +x[1], treemap) : [dx, dy]; }; treemap.tile = function(x) { return arguments.length ? (tile = required(x), treemap) : tile; }; treemap.padding = function(x) { return arguments.length ? treemap.paddingInner(x).paddingOuter(x) : treemap.paddingInner(); }; treemap.paddingInner = function(x) { return arguments.length ? (paddingInner = typeof x === "function" ? x : constant$1(+x), treemap) : paddingInner; }; treemap.paddingOuter = function(x) { return arguments.length ? treemap.paddingTop(x).paddingRight(x).paddingBottom(x).paddingLeft(x) : treemap.paddingTop(); }; treemap.paddingTop = function(x) { return arguments.length ? (paddingTop = typeof x === "function" ? x : constant$1(+x), treemap) : paddingTop; }; treemap.paddingRight = function(x) { return arguments.length ? (paddingRight = typeof x === "function" ? x : constant$1(+x), treemap) : paddingRight; }; treemap.paddingBottom = function(x) { return arguments.length ? (paddingBottom = typeof x === "function" ? x : constant$1(+x), treemap) : paddingBottom; }; treemap.paddingLeft = function(x) { return arguments.length ? (paddingLeft = typeof x === "function" ? x : constant$1(+x), treemap) : paddingLeft; }; return treemap; } var xhtml = "http://www.w3.org/1999/xhtml"; var namespaces = { svg: "http://www.w3.org/2000/svg", xhtml: xhtml, xlink: "http://www.w3.org/1999/xlink", xml: "http://www.w3.org/XML/1998/namespace", xmlns: "http://www.w3.org/2000/xmlns/" }; function namespace(name) { var prefix = name += "", i = prefix.indexOf(":"); if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1); return namespaces.hasOwnProperty(prefix) ? {space: namespaces[prefix], local: name} : name; } function creatorInherit(name) { return function() { var document = this.ownerDocument, uri = this.namespaceURI; return uri === xhtml && document.documentElement.namespaceURI === xhtml ? document.createElement(name) : document.createElementNS(uri, name); }; } function creatorFixed(fullname) { return function() { return this.ownerDocument.createElementNS(fullname.space, fullname.local); }; } function creator(name) { var fullname = namespace(name); return (fullname.local ? creatorFixed : creatorInherit)(fullname); } function none() {} function selector(selector) { return selector == null ? none : function() { return this.querySelector(selector); }; } function selection_select(select) { if (typeof select !== "function") select = selector(select); for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) { if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) { if ("__data__" in node) subnode.__data__ = node.__data__; subgroup[i] = subnode; } } } return new Selection(subgroups, this._parents); } function empty$1() { return []; } function selectorAll(selector) { return selector == null ? empty$1 : function() { return this.querySelectorAll(selector); }; } function selection_selectAll(select) { if (typeof select !== "function") select = selectorAll(select); for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) { for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { if (node = group[i]) { subgroups.push(select.call(node, node.__data__, i, group)); parents.push(node); } } } return new Selection(subgroups, parents); } function matcher(selector) { return function() { return this.matches(selector); }; } function selection_filter(match) { if (typeof match !== "function") match = matcher(match); for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) { if ((node = group[i]) && match.call(node, node.__data__, i, group)) { subgroup.push(node); } } } return new Selection(subgroups, this._parents); } function sparse(update) { return new Array(update.length); } function selection_enter() { return new Selection(this._enter || this._groups.map(sparse), this._parents); } function EnterNode(parent, datum) { this.ownerDocument = parent.ownerDocument; this.namespaceURI = parent.namespaceURI; this._next = null; this._parent = parent; this.__data__ = datum; } EnterNode.prototype = { constructor: EnterNode, appendChild: function(child) { return this._parent.insertBefore(child, this._next); }, insertBefore: function(child, next) { return this._parent.insertBefore(child, next); }, querySelector: function(selector) { return this._parent.querySelector(selector); }, querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); } }; function constant$2(x) { return function() { return x; }; } var keyPrefix = "$"; // Protect against keys like “__proto__”. function bindIndex(parent, group, enter, update, exit, data) { var i = 0, node, groupLength = group.length, dataLength = data.length; // Put any non-null nodes that fit into update. // Put any null nodes into enter. // Put any remaining data into enter. for (; i < dataLength; ++i) { if (node = group[i]) { node.__data__ = data[i]; update[i] = node; } else { enter[i] = new EnterNode(parent, data[i]); } } // Put any non-null nodes that don’t fit into exit. for (; i < groupLength; ++i) { if (node = group[i]) { exit[i] = node; } } } function bindKey(parent, group, enter, update, exit, data, key) { var i, node, nodeByKeyValue = {}, groupLength = group.length, dataLength = data.length, keyValues = new Array(groupLength), keyValue; // Compute the key for each node. // If multiple nodes have the same key, the duplicates are added to exit. for (i = 0; i < groupLength; ++i) { if (node = group[i]) { keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group); if (keyValue in nodeByKeyValue) { exit[i] = node; } else { nodeByKeyValue[keyValue] = node; } } } // Compute the key for each datum. // If there a node associated with this key, join and add it to update. // If there is not (or the key is a duplicate), add it to enter. for (i = 0; i < dataLength; ++i) { keyValue = keyPrefix + key.call(parent, data[i], i, data); if (node = nodeByKeyValue[keyValue]) { update[i] = node; node.__data__ = data[i]; nodeByKeyValue[keyValue] = null; } else { enter[i] = new EnterNode(parent, data[i]); } } // Add any remaining nodes that were not bound to data to exit. for (i = 0; i < groupLength; ++i) { if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) { exit[i] = node; } } } function selection_data(value, key) { if (!value) { data = new Array(this.size()), j = -1; this.each(function(d) { data[++j] = d; }); return data; } var bind = key ? bindKey : bindIndex, parents = this._parents, groups = this._groups; if (typeof value !== "function") value = constant$2(value); for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) { var parent = parents[j], group = groups[j], groupLength = group.length, data = value.call(parent, parent && parent.__data__, j, parents), dataLength = data.length, enterGroup = enter[j] = new Array(dataLength), updateGroup = update[j] = new Array(dataLength), exitGroup = exit[j] = new Array(groupLength); bind(parent, group, enterGroup, updateGroup, exitGroup, data, key); // Now connect the enter nodes to their following update node, such that // appendChild can insert the materialized enter node before this node, // rather than at the end of the parent node. for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) { if (previous = enterGroup[i0]) { if (i0 >= i1) i1 = i0 + 1; while (!(next = updateGroup[i1]) && ++i1 < dataLength); previous._next = next || null; } } } update = new Selection(update, parents); update._enter = enter; update._exit = exit; return update; } function selection_exit() { return new Selection(this._exit || this._groups.map(sparse), this._parents); } function selection_join(onenter, onupdate, onexit) { var enter = this.enter(), update = this, exit = this.exit(); enter = typeof onenter === "function" ? onenter(enter) : enter.append(onenter + ""); if (onupdate != null) update = onupdate(update); if (onexit == null) exit.remove(); else onexit(exit); return enter && update ? enter.merge(update).order() : update; } function selection_merge(selection) { for (var groups0 = this._groups, groups1 = selection._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) { for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) { if (node = group0[i] || group1[i]) { merge[i] = node; } } } for (; j < m0; ++j) { merges[j] = groups0[j]; } return new Selection(merges, this._parents); } function selection_order() { for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) { for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) { if (node = group[i]) { if (next && node.compareDocumentPosition(next) ^ 4) next.parentNode.insertBefore(node, next); next = node; } } } return this; } function selection_sort(compare) { if (!compare) compare = ascending$1; function compareNode(a, b) { return a && b ? compare(a.__data__, b.__data__) : !a - !b; } for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) { for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) { if (node = group[i]) { sortgroup[i] = node; } } sortgroup.sort(compareNode); } return new Selection(sortgroups, this._parents).order(); } function ascending$1(a, b) { return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; } function selection_call() { var callback = arguments[0]; arguments[0] = this; callback.apply(null, arguments); return this; } function selection_nodes() { var nodes = new Array(this.size()), i = -1; this.each(function() { nodes[++i] = this; }); return nodes; } function selection_node() { for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { for (var group = groups[j], i = 0, n = group.length; i < n; ++i) { var node = group[i]; if (node) return node; } } return null; } function selection_size() { var size = 0; this.each(function() { ++size; }); return size; } function selection_empty() { return !this.node(); } function selection_each(callback) { for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) { if (node = group[i]) callback.call(node, node.__data__, i, group); } } return this; } function attrRemove(name) { return function() { this.removeAttribute(name); }; } function attrRemoveNS(fullname) { return function() { this.removeAttributeNS(fullname.space, fullname.local); }; } function attrConstant(name, value) { return function() { this.setAttribute(name, value); }; } function attrConstantNS(fullname, value) { return function() { this.setAttributeNS(fullname.space, fullname.local, value); }; } function attrFunction(name, value) { return function() { var v = value.apply(this, arguments); if (v == null) this.removeAttribute(name); else this.setAttribute(name, v); }; } function attrFunctionNS(fullname, value) { return function() { var v = value.apply(this, arguments); if (v == null) this.removeAttributeNS(fullname.space, fullname.local); else this.setAttributeNS(fullname.space, fullname.local, v); }; } function selection_attr(name, value) { var fullname = namespace(name); if (arguments.length < 2) { var node = this.node(); return fullname.local ? node.getAttributeNS(fullname.space, fullname.local) : node.getAttribute(fullname); } return this.each((value == null ? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === "function" ? (fullname.local ? attrFunctionNS : attrFunction) : (fullname.local ? attrConstantNS : attrConstant)))(fullname, value)); } function defaultView(node) { return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node || (node.document && node) // node is a Window || node.defaultView; // node is a Document } function styleRemove(name) { return function() { this.style.removeProperty(name); }; } function styleConstant(name, value, priority) { return function() { this.style.setProperty(name, value, priority); }; } function styleFunction(name, value, priority) { return function() { var v = value.apply(this, arguments); if (v == null) this.style.removeProperty(name); else this.style.setProperty(name, v, priority); }; } function selection_style(name, value, priority) { return arguments.length > 1 ? this.each((value == null ? styleRemove : typeof value === "function" ? styleFunction : styleConstant)(name, value, priority == null ? "" : priority)) : styleValue(this.node(), name); } function styleValue(node, name) { return node.style.getPropertyValue(name) || defaultView(node).getComputedStyle(node, null).getPropertyValue(name); } function propertyRemove(name) { return function() { delete this[name]; }; } function propertyConstant(name, value) { return function() { this[name] = value; }; } function propertyFunction(name, value) { return function() { var v = value.apply(this, arguments); if (v == null) delete this[name]; else this[name] = v; }; } function selection_property(name, value) { return arguments.length > 1 ? this.each((value == null ? propertyRemove : typeof value === "function" ? propertyFunction : propertyConstant)(name, value)) : this.node()[name]; } function classArray(string) { return string.trim().split(/^|\s+/); } function classList(node) { return node.classList || new ClassList(node); } function ClassList(node) { this._node = node; this._names = classArray(node.getAttribute("class") || ""); } ClassList.prototype = { add: function(name) { var i = this._names.indexOf(name); if (i < 0) { this._names.push(name); this._node.setAttribute("class", this._names.join(" ")); } }, remove: function(name) { var i = this._names.indexOf(name); if (i >= 0) { this._names.splice(i, 1); this._node.setAttribute("class", this._names.join(" ")); } }, contains: function(name) { return this._names.indexOf(name) >= 0; } }; function classedAdd(node, names) { var list = classList(node), i = -1, n = names.length; while (++i < n) list.add(names[i]); } function classedRemove(node, names) { var list = classList(node), i = -1, n = names.length; while (++i < n) list.remove(names[i]); } function classedTrue(names) { return function() { classedAdd(this, names); }; } function classedFalse(names) { return function() { classedRemove(this, names); }; } function classedFunction(names, value) { return function() { (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names); }; } function selection_classed(name, value) { var names = classArray(name + ""); if (arguments.length < 2) { var list = classList(this.node()), i = -1, n = names.length; while (++i < n) if (!list.contains(names[i])) return false; return true; } return this.each((typeof value === "function" ? classedFunction : value ? classedTrue : classedFalse)(names, value)); } function textRemove() { this.textContent = ""; } function textConstant(value) { return function() { this.textContent = value; }; } function textFunction(value) { return function() { var v = value.apply(this, arguments); this.textContent = v == null ? "" : v; }; } function selection_text(value) { return arguments.length ? this.each(value == null ? textRemove : (typeof value === "function" ? textFunction : textConstant)(value)) : this.node().textContent; } function htmlRemove() { this.innerHTML = ""; } function htmlConstant(value) { return function() { this.innerHTML = value; }; } function htmlFunction(value) { return function() { var v = value.apply(this, arguments); this.innerHTML = v == null ? "" : v; }; } function selection_html(value) { return arguments.length ? this.each(value == null ? htmlRemove : (typeof value === "function" ? htmlFunction : htmlConstant)(value)) : this.node().innerHTML; } function raise() { if (this.nextSibling) this.parentNode.appendChild(this); } function selection_raise() { return this.each(raise); } function lower() { if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild); } function selection_lower() { return this.each(lower); } function selection_append(name) { var create = typeof name === "function" ? name : creator(name); return this.select(function() { return this.appendChild(create.apply(this, arguments)); }); } function constantNull() { return null; } function selection_insert(name, before) { var create = typeof name === "function" ? name : creator(name), select = before == null ? constantNull : typeof before === "function" ? before : selector(before); return this.select(function() { return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null); }); } function remove() { var parent = this.parentNode; if (parent) parent.removeChild(this); } function selection_remove() { return this.each(remove); } function selection_cloneShallow() { var clone = this.cloneNode(false), parent = this.parentNode; return parent ? parent.insertBefore(clone, this.nextSibling) : clone; } function selection_cloneDeep() { var clone = this.cloneNode(true), parent = this.parentNode; return parent ? parent.insertBefore(clone, this.nextSibling) : clone; } function selection_clone(deep) { return this.select(deep ? selection_cloneDeep : selection_cloneShallow); } function selection_datum(value) { return arguments.length ? this.property("__data__", value) : this.node().__data__; } var filterEvents = {}; var event = null; if (typeof document !== "undefined") { var element$1 = document.documentElement; if (!("onmouseenter" in element$1)) { filterEvents = {mouseenter: "mouseover", mouseleave: "mouseout"}; } } function filterContextListener(listener, index, group) { listener = contextListener(listener, index, group); return function(event) { var related = event.relatedTarget; if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) { listener.call(this, event); } }; } function contextListener(listener, index, group) { return function(event1) { var event0 = event; // Events can be reentrant (e.g., focus). event = event1; try { listener.call(this, this.__data__, index, group); } finally { event = event0; } }; } function parseTypenames(typenames) { return typenames.trim().split(/^|\s+/).map(function(t) { var name = "", i = t.indexOf("."); if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i); return {type: t, name: name}; }); } function onRemove(typename) { return function() { var on = this.__on; if (!on) return; for (var j = 0, i = -1, m = on.length, o; j < m; ++j) { if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) { this.removeEventListener(o.type, o.listener, o.capture); } else { on[++i] = o; } } if (++i) on.length = i; else delete this.__on; }; } function onAdd(typename, value, capture) { var wrap = filterEvents.hasOwnProperty(typename.type) ? filterContextListener : contextListener; return function(d, i, group) { var on = this.__on, o, listener = wrap(value, i, group); if (on) for (var j = 0, m = on.length; j < m; ++j) { if ((o = on[j]).type === typename.type && o.name === typename.name) { this.removeEventListener(o.type, o.listener, o.capture); this.addEventListener(o.type, o.listener = listener, o.capture = capture); o.value = value; return; } } this.addEventListener(typename.type, listener, capture); o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture}; if (!on) this.__on = [o]; else on.push(o); }; } function selection_on(typename, value, capture) { var typenames = parseTypenames(typename + ""), i, n = typenames.length, t; if (arguments.length < 2) { var on = this.node().__on; if (on) for (var j = 0, m = on.length, o; j < m; ++j) { for (i = 0, o = on[j]; i < n; ++i) { if ((t = typenames[i]).type === o.type && t.name === o.name) { return o.value; } } } return; } on = value ? onAdd : onRemove; if (capture == null) capture = false; for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture)); return this; } function dispatchEvent(node, type, params) { var window = defaultView(node), event = window.CustomEvent; if (typeof event === "function") { event = new event(type, params); } else { event = window.document.createEvent("Event"); if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail; else event.initEvent(type, false, false); } node.dispatchEvent(event); } function dispatchConstant(type, params) { return function() { return dispatchEvent(this, type, params); }; } function dispatchFunction(type, params) { return function() { return dispatchEvent(this, type, params.apply(this, arguments)); }; } function selection_dispatch(type, params) { return this.each((typeof params === "function" ? dispatchFunction : dispatchConstant)(type, params)); } var root = [null]; function Selection(groups, parents) { this._groups = groups; this._parents = parents; } function selection() { return new Selection([[document.documentElement]], root); } Selection.prototype = selection.prototype = { constructor: Selection, select: selection_select, selectAll: selection_selectAll, filter: selection_filter, data: selection_data, enter: selection_enter, exit: selection_exit, join: selection_join, merge: selection_merge, order: selection_order, sort: selection_sort, call: selection_call, nodes: selection_nodes, node: selection_node, size: selection_size, empty: selection_empty, each: selection_each, attr: selection_attr, style: selection_style, property: selection_property, classed: selection_classed, text: selection_text, html: selection_html, raise: selection_raise, lower: selection_lower, append: selection_append, insert: selection_insert, remove: selection_remove, clone: selection_clone, datum: selection_datum, on: selection_on, dispatch: selection_dispatch }; function select$1(selector) { return typeof selector === "string" ? new Selection([[document.querySelector(selector)]], [document.documentElement]) : new Selection([[selector]], root); } function clientPoint(node, event) { var svg = node.ownerSVGElement || node; if (svg.createSVGPoint) { var point = svg.createSVGPoint(); point.x = event.clientX, point.y = event.clientY; point = point.matrixTransform(node.getScreenCTM().inverse()); return [point.x, point.y]; } var rect = node.getBoundingClientRect(); return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop]; } var noop$1 = {value: function() {}}; function dispatch() { for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) { if (!(t = arguments[i] + "") || (t in _) || /[\s.]/.test(t)) throw new Error("illegal type: " + t); _[t] = []; } return new Dispatch(_); } function Dispatch(_) { this._ = _; } function parseTypenames$1(typenames, types) { return typenames.trim().split(/^|\s+/).map(function(t) { var name = "", i = t.indexOf("."); if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i); if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t); return {type: t, name: name}; }); } Dispatch.prototype = dispatch.prototype = { constructor: Dispatch, on: function(typename, callback) { var _ = this._, T = parseTypenames$1(typename + "", _), t, i = -1, n = T.length; // If no callback was specified, return the callback of the given type and name. if (arguments.length < 2) { while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t; return; } // If a type was specified, set the callback for the given type and name. // Otherwise, if a null callback was specified, remove callbacks of the given name. if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback); while (++i < n) { if (t = (typename = T[i]).type) _[t] = set(_[t], typename.name, callback); else if (callback == null) for (t in _) _[t] = set(_[t], typename.name, null); } return this; }, copy: function() { var copy = {}, _ = this._; for (var t in _) copy[t] = _[t].slice(); return new Dispatch(copy); }, call: function(type, that) { if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2]; if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type); for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args); }, apply: function(type, that, args) { if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type); for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args); } }; function get(type, name) { for (var i = 0, n = type.length, c; i < n; ++i) { if ((c = type[i]).name === name) { return c.value; } } } function set(type, name, callback) { for (var i = 0, n = type.length; i < n; ++i) { if (type[i].name === name) { type[i] = noop$1, type = type.slice(0, i).concat(type.slice(i + 1)); break; } } if (callback != null) type.push({name: name, value: callback}); return type; } var frame = 0, // is an animation frame pending? timeout = 0, // is a timeout pending? interval$1 = 0, // are any timers active? pokeDelay = 1000, // how frequently we check for clock skew taskHead, taskTail, clockLast = 0, clockNow = 0, clockSkew = 0, clock = typeof performance === "object" && performance.now ? performance : Date, setFrame = typeof window === "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function(f) { setTimeout(f, 17); }; function now() { return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew); } function clearNow() { clockNow = 0; } function Timer() { this._call = this._time = this._next = null; } Timer.prototype = timer.prototype = { constructor: Timer, restart: function(callback, delay, time) { if (typeof callback !== "function") throw new TypeError("callback is not a function"); time = (time == null ? now() : +time) + (delay == null ? 0 : +delay); if (!this._next && taskTail !== this) { if (taskTail) taskTail._next = this; else taskHead = this; taskTail = this; } this._call = callback; this._time = time; sleep(); }, stop: function() { if (this._call) { this._call = null; this._time = Infinity; sleep(); } } }; function timer(callback, delay, time) { var t = new Timer; t.restart(callback, delay, time); return t; } function timerFlush() { now(); // Get the current time, if not already set. ++frame; // Pretend we’ve set an alarm, if we haven’t already. var t = taskHead, e; while (t) { if ((e = clockNow - t._time) >= 0) t._call.call(null, e); t = t._next; } --frame; } function wake() { clockNow = (clockLast = clock.now()) + clockSkew; frame = timeout = 0; try { timerFlush(); } finally { frame = 0; nap(); clockNow = 0; } } function poke() { var now = clock.now(), delay = now - clockLast; if (delay > pokeDelay) clockSkew -= delay, clockLast = now; } function nap() { var t0, t1 = taskHead, t2, time = Infinity; while (t1) { if (t1._call) { if (time > t1._time) time = t1._time; t0 = t1, t1 = t1._next; } else { t2 = t1._next, t1._next = null; t1 = t0 ? t0._next = t2 : taskHead = t2; } } taskTail = t0; sleep(time); } function sleep(time) { if (frame) return; // Soonest alarm already set, or will be. if (timeout) timeout = clearTimeout(timeout); var delay = time - clockNow; // Strictly less than if we recomputed clockNow. if (delay > 24) { if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew); if (interval$1) interval$1 = clearInterval(interval$1); } else { if (!interval$1) clockLast = clock.now(), interval$1 = setInterval(poke, pokeDelay); frame = 1, setFrame(wake); } } function timeout$1(callback, delay, time) { var t = new Timer; delay = delay == null ? 0 : +delay; t.restart(function(elapsed) { t.stop(); callback(elapsed + delay); }, delay, time); return t; } var emptyOn = dispatch("start", "end", "cancel", "interrupt"); var emptyTween = []; var CREATED = 0; var SCHEDULED = 1; var STARTING = 2; var STARTED = 3; var RUNNING = 4; var ENDING = 5; var ENDED = 6; function schedule(node, name, id, index, group, timing) { var schedules = node.__transition; if (!schedules) node.__transition = {}; else if (id in schedules) return; create(node, id, { name: name, index: index, // For context during callback. group: group, // For context during callback. on: emptyOn, tween: emptyTween, time: timing.time, delay: timing.delay, duration: timing.duration, ease: timing.ease, timer: null, state: CREATED }); } function init$1(node, id) { var schedule = get$1(node, id); if (schedule.state > CREATED) throw new Error("too late; already scheduled"); return schedule; } function set$1(node, id) { var schedule = get$1(node, id); if (schedule.state > STARTED) throw new Error("too late; already running"); return schedule; } function get$1(node, id) { var schedule = node.__transition; if (!schedule || !(schedule = schedule[id])) throw new Error("transition not found"); return schedule; } function create(node, id, self) { var schedules = node.__transition, tween; // Initialize the self timer when the transition is created. // Note the actual delay is not known until the first callback! schedules[id] = self; self.timer = timer(schedule, 0, self.time); function schedule(elapsed) { self.state = SCHEDULED; self.timer.restart(start, self.delay, self.time); // If the elapsed delay is less than our first sleep, start immediately. if (self.delay <= elapsed) start(elapsed - self.delay); } function start(elapsed) { var i, j, n, o; // If the state is not SCHEDULED, then we previously errored on start. if (self.state !== SCHEDULED) return stop(); for (i in schedules) { o = schedules[i]; if (o.name !== self.name) continue; // While this element already has a starting transition during this frame, // defer starting an interrupting transition until that transition has a // chance to tick (and possibly end); see d3/d3-transition#54! if (o.state === STARTED) return timeout$1(start); // Interrupt the active transition, if any. if (o.state === RUNNING) { o.state = ENDED; o.timer.stop(); o.on.call("interrupt", node, node.__data__, o.index, o.group); delete schedules[i]; } // Cancel any pre-empted transitions. else if (+i < id) { o.state = ENDED; o.timer.stop(); o.on.call("cancel", node, node.__data__, o.index, o.group); delete schedules[i]; } } // Defer the first tick to end of the current frame; see d3/d3#1576. // Note the transition may be canceled after start and before the first tick! // Note this must be scheduled before the start event; see d3/d3-transition#16! // Assuming this is successful, subsequent callbacks go straight to tick. timeout$1(function() { if (self.state === STARTED) { self.state = RUNNING; self.timer.restart(tick, self.delay, self.time); tick(elapsed); } }); // Dispatch the start event. // Note this must be done before the tween are initialized. self.state = STARTING; self.on.call("start", node, node.__data__, self.index, self.group); if (self.state !== STARTING) return; // interrupted self.state = STARTED; // Initialize the tween, deleting null tween. tween = new Array(n = self.tween.length); for (i = 0, j = -1; i < n; ++i) { if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) { tween[++j] = o; } } tween.length = j + 1; } function tick(elapsed) { var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1), i = -1, n = tween.length; while (++i < n) { tween[i].call(node, t); } // Dispatch the end event. if (self.state === ENDING) { self.on.call("end", node, node.__data__, self.index, self.group); stop(); } } function stop() { self.state = ENDED; self.timer.stop(); delete schedules[id]; for (var i in schedules) return; // eslint-disable-line no-unused-vars delete node.__transition; } } function interrupt(node, name) { var schedules = node.__transition, schedule, active, empty = true, i; if (!schedules) return; name = name == null ? null : name + ""; for (i in schedules) { if ((schedule = schedules[i]).name !== name) { empty = false; continue; } active = schedule.state > STARTING && schedule.state < ENDING; schedule.state = ENDED; schedule.timer.stop(); schedule.on.call(active ? "interrupt" : "cancel", node, node.__data__, schedule.index, schedule.group); delete schedules[i]; } if (empty) delete node.__transition; } function selection_interrupt(name) { return this.each(function() { interrupt(this, name); }); } function define(constructor, factory, prototype) { constructor.prototype = factory.prototype = prototype; prototype.constructor = constructor; } function extend(parent, definition) { var prototype = Object.create(parent.prototype); for (var key in definition) prototype[key] = definition[key]; return prototype; } function Color() {} var darker = 0.7; var brighter = 1 / darker; var reI = "\\s*([+-]?\\d+)\\s*", reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*", reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*", reHex = /^#([0-9a-f]{3,8})$/, reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"), reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"), reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"), reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"), reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"), reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$"); var named = { aliceblue: 0xf0f8ff, antiquewhite: 0xfaebd7, aqua: 0x00ffff, aquamarine: 0x7fffd4, azure: 0xf0ffff, beige: 0xf5f5dc, bisque: 0xffe4c4, black: 0x000000, blanchedalmond: 0xffebcd, blue: 0x0000ff, blueviolet: 0x8a2be2, brown: 0xa52a2a, burlywood: 0xdeb887, cadetblue: 0x5f9ea0, chartreuse: 0x7fff00, chocolate: 0xd2691e, coral: 0xff7f50, cornflowerblue: 0x6495ed, cornsilk: 0xfff8dc, crimson: 0xdc143c, cyan: 0x00ffff, darkblue: 0x00008b, darkcyan: 0x008b8b, darkgoldenrod: 0xb8860b, darkgray: 0xa9a9a9, darkgreen: 0x006400, darkgrey: 0xa9a9a9, darkkhaki: 0xbdb76b, darkmagenta: 0x8b008b, darkolivegreen: 0x556b2f, darkorange: 0xff8c00, darkorchid: 0x9932cc, darkred: 0x8b0000, darksalmon: 0xe9967a, darkseagreen: 0x8fbc8f, darkslateblue: 0x483d8b, darkslategray: 0x2f4f4f, darkslategrey: 0x2f4f4f, darkturquoise: 0x00ced1, darkviolet: 0x9400d3, deeppink: 0xff1493, deepskyblue: 0x00bfff, dimgray: 0x696969, dimgrey: 0x696969, dodgerblue: 0x1e90ff, firebrick: 0xb22222, floralwhite: 0xfffaf0, forestgreen: 0x228b22, fuchsia: 0xff00ff, gainsboro: 0xdcdcdc, ghostwhite: 0xf8f8ff, gold: 0xffd700, goldenrod: 0xdaa520, gray: 0x808080, green: 0x008000, greenyellow: 0xadff2f, grey: 0x808080, honeydew: 0xf0fff0, hotpink: 0xff69b4, indianred: 0xcd5c5c, indigo: 0x4b0082, ivory: 0xfffff0, khaki: 0xf0e68c, lavender: 0xe6e6fa, lavenderblush: 0xfff0f5, lawngreen: 0x7cfc00, lemonchiffon: 0xfffacd, lightblue: 0xadd8e6, lightcoral: 0xf08080, lightcyan: 0xe0ffff, lightgoldenrodyellow: 0xfafad2, lightgray: 0xd3d3d3, lightgreen: 0x90ee90, lightgrey: 0xd3d3d3, lightpink: 0xffb6c1, lightsalmon: 0xffa07a, lightseagreen: 0x20b2aa, lightskyblue: 0x87cefa, lightslategray: 0x778899, lightslategrey: 0x778899, lightsteelblue: 0xb0c4de, lightyellow: 0xffffe0, lime: 0x00ff00, limegreen: 0x32cd32, linen: 0xfaf0e6, magenta: 0xff00ff, maroon: 0x800000, mediumaquamarine: 0x66cdaa, mediumblue: 0x0000cd, mediumorchid: 0xba55d3, mediumpurple: 0x9370db, mediumseagreen: 0x3cb371, mediumslateblue: 0x7b68ee, mediumspringgreen: 0x00fa9a, mediumturquoise: 0x48d1cc, mediumvioletred: 0xc71585, midnightblue: 0x191970, mintcream: 0xf5fffa, mistyrose: 0xffe4e1, moccasin: 0xffe4b5, navajowhite: 0xffdead, navy: 0x000080, oldlace: 0xfdf5e6, olive: 0x808000, olivedrab: 0x6b8e23, orange: 0xffa500, orangered: 0xff4500, orchid: 0xda70d6, palegoldenrod: 0xeee8aa, palegreen: 0x98fb98, paleturquoise: 0xafeeee, palevioletred: 0xdb7093, papayawhip: 0xffefd5, peachpuff: 0xffdab9, peru: 0xcd853f, pink: 0xffc0cb, plum: 0xdda0dd, powderblue: 0xb0e0e6, purple: 0x800080, rebeccapurple: 0x663399, red: 0xff0000, rosybrown: 0xbc8f8f, royalblue: 0x4169e1, saddlebrown: 0x8b4513, salmon: 0xfa8072, sandybrown: 0xf4a460, seagreen: 0x2e8b57, seashell: 0xfff5ee, sienna: 0xa0522d, silver: 0xc0c0c0, skyblue: 0x87ceeb, slateblue: 0x6a5acd, slategray: 0x708090, slategrey: 0x708090, snow: 0xfffafa, springgreen: 0x00ff7f, steelblue: 0x4682b4, tan: 0xd2b48c, teal: 0x008080, thistle: 0xd8bfd8, tomato: 0xff6347, turquoise: 0x40e0d0, violet: 0xee82ee, wheat: 0xf5deb3, white: 0xffffff, whitesmoke: 0xf5f5f5, yellow: 0xffff00, yellowgreen: 0x9acd32 }; define(Color, color, { copy: function(channels) { return Object.assign(new this.constructor, this, channels); }, displayable: function() { return this.rgb().displayable(); }, hex: color_formatHex, // Deprecated! Use color.formatHex. formatHex: color_formatHex, formatHsl: color_formatHsl, formatRgb: color_formatRgb, toString: color_formatRgb }); function color_formatHex() { return this.rgb().formatHex(); } function color_formatHsl() { return hslConvert(this).formatHsl(); } function color_formatRgb() { return this.rgb().formatRgb(); } function color(format) { var m, l; format = (format + "").trim().toLowerCase(); return (m = reHex.exec(format)) ? (l = m[1].length, m = parseInt(m[1], 16), l === 6 ? rgbn(m) // #ff0000 : l === 3 ? new Rgb((m >> 8 & 0xf) | (m >> 4 & 0xf0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1) // #f00 : l === 8 ? new Rgb(m >> 24 & 0xff, m >> 16 & 0xff, m >> 8 & 0xff, (m & 0xff) / 0xff) // #ff000000 : l === 4 ? new Rgb((m >> 12 & 0xf) | (m >> 8 & 0xf0), (m >> 8 & 0xf) | (m >> 4 & 0xf0), (m >> 4 & 0xf) | (m & 0xf0), (((m & 0xf) << 4) | (m & 0xf)) / 0xff) // #f000 : null) // invalid hex : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0) : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%) : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1) : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1) : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%) : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1) : named.hasOwnProperty(format) ? rgbn(named[format]) // eslint-disable-line no-prototype-builtins : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0) : null; } function rgbn(n) { return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1); } function rgba(r, g, b, a) { if (a <= 0) r = g = b = NaN; return new Rgb(r, g, b, a); } function rgbConvert(o) { if (!(o instanceof Color)) o = color(o); if (!o) return new Rgb; o = o.rgb(); return new Rgb(o.r, o.g, o.b, o.opacity); } function rgb(r, g, b, opacity) { return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity); } function Rgb(r, g, b, opacity) { this.r = +r; this.g = +g; this.b = +b; this.opacity = +opacity; } define(Rgb, rgb, extend(Color, { brighter: function(k) { k = k == null ? brighter : Math.pow(brighter, k); return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity); }, darker: function(k) { k = k == null ? darker : Math.pow(darker, k); return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity); }, rgb: function() { return this; }, displayable: function() { return (-0.5 <= this.r && this.r < 255.5) && (-0.5 <= this.g && this.g < 255.5) && (-0.5 <= this.b && this.b < 255.5) && (0 <= this.opacity && this.opacity <= 1); }, hex: rgb_formatHex, // Deprecated! Use color.formatHex. formatHex: rgb_formatHex, formatRgb: rgb_formatRgb, toString: rgb_formatRgb })); function rgb_formatHex() { return "#" + hex(this.r) + hex(this.g) + hex(this.b); } function rgb_formatRgb() { var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a)); return (a === 1 ? "rgb(" : "rgba(") + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + ", " + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + ", " + Math.max(0, Math.min(255, Math.round(this.b) || 0)) + (a === 1 ? ")" : ", " + a + ")"); } function hex(value) { value = Math.max(0, Math.min(255, Math.round(value) || 0)); return (value < 16 ? "0" : "") + value.toString(16); } function hsla(h, s, l, a) { if (a <= 0) h = s = l = NaN; else if (l <= 0 || l >= 1) h = s = NaN; else if (s <= 0) h = NaN; return new Hsl(h, s, l, a); } function hslConvert(o) { if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity); if (!(o instanceof Color)) o = color(o); if (!o) return new Hsl; if (o instanceof Hsl) return o; o = o.rgb(); var r = o.r / 255, g = o.g / 255, b = o.b / 255, min = Math.min(r, g, b), max = Math.max(r, g, b), h = NaN, s = max - min, l = (max + min) / 2; if (s) { if (r === max) h = (g - b) / s + (g < b) * 6; else if (g === max) h = (b - r) / s + 2; else h = (r - g) / s + 4; s /= l < 0.5 ? max + min : 2 - max - min; h *= 60; } else { s = l > 0 && l < 1 ? 0 : h; } return new Hsl(h, s, l, o.opacity); } function hsl(h, s, l, opacity) { return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity); } function Hsl(h, s, l, opacity) { this.h = +h; this.s = +s; this.l = +l; this.opacity = +opacity; } define(Hsl, hsl, extend(Color, { brighter: function(k) { k = k == null ? brighter : Math.pow(brighter, k); return new Hsl(this.h, this.s, this.l * k, this.opacity); }, darker: function(k) { k = k == null ? darker : Math.pow(darker, k); return new Hsl(this.h, this.s, this.l * k, this.opacity); }, rgb: function() { var h = this.h % 360 + (this.h < 0) * 360, s = isNaN(h) || isNaN(this.s) ? 0 : this.s, l = this.l, m2 = l + (l < 0.5 ? l : 1 - l) * s, m1 = 2 * l - m2; return new Rgb( hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2), hsl2rgb(h, m1, m2), hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2), this.opacity ); }, displayable: function() { return (0 <= this.s && this.s <= 1 || isNaN(this.s)) && (0 <= this.l && this.l <= 1) && (0 <= this.opacity && this.opacity <= 1); }, formatHsl: function() { var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a)); return (a === 1 ? "hsl(" : "hsla(") + (this.h || 0) + ", " + (this.s || 0) * 100 + "%, " + (this.l || 0) * 100 + "%" + (a === 1 ? ")" : ", " + a + ")"); } })); /* From FvD 13.37, CSS Color Module Level 3 */ function hsl2rgb(h, m1, m2) { return (h < 60 ? m1 + (m2 - m1) * h / 60 : h < 180 ? m2 : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60 : m1) * 255; } var deg2rad = Math.PI / 180; var rad2deg = 180 / Math.PI; // https://observablehq.com/@mbostock/lab-and-rgb var K = 18, Xn = 0.96422, Yn = 1, Zn = 0.82521, t0 = 4 / 29, t1 = 6 / 29, t2 = 3 * t1 * t1, t3 = t1 * t1 * t1; function labConvert(o) { if (o instanceof Lab) return new Lab(o.l, o.a, o.b, o.opacity); if (o instanceof Hcl) return hcl2lab(o); if (!(o instanceof Rgb)) o = rgbConvert(o); var r = rgb2lrgb(o.r), g = rgb2lrgb(o.g), b = rgb2lrgb(o.b), y = xyz2lab((0.2225045 * r + 0.7168786 * g + 0.0606169 * b) / Yn), x, z; if (r === g && g === b) x = z = y; else { x = xyz2lab((0.4360747 * r + 0.3850649 * g + 0.1430804 * b) / Xn); z = xyz2lab((0.0139322 * r + 0.0971045 * g + 0.7141733 * b) / Zn); } return new Lab(116 * y - 16, 500 * (x - y), 200 * (y - z), o.opacity); } function lab(l, a, b, opacity) { return arguments.length === 1 ? labConvert(l) : new Lab(l, a, b, opacity == null ? 1 : opacity); } function Lab(l, a, b, opacity) { this.l = +l; this.a = +a; this.b = +b; this.opacity = +opacity; } define(Lab, lab, extend(Color, { brighter: function(k) { return new Lab(this.l + K * (k == null ? 1 : k), this.a, this.b, this.opacity); }, darker: function(k) { return new Lab(this.l - K * (k == null ? 1 : k), this.a, this.b, this.opacity); }, rgb: function() { var y = (this.l + 16) / 116, x = isNaN(this.a) ? y : y + this.a / 500, z = isNaN(this.b) ? y : y - this.b / 200; x = Xn * lab2xyz(x); y = Yn * lab2xyz(y); z = Zn * lab2xyz(z); return new Rgb( lrgb2rgb( 3.1338561 * x - 1.6168667 * y - 0.4906146 * z), lrgb2rgb(-0.9787684 * x + 1.9161415 * y + 0.0334540 * z), lrgb2rgb( 0.0719453 * x - 0.2289914 * y + 1.4052427 * z), this.opacity ); } })); function xyz2lab(t) { return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0; } function lab2xyz(t) { return t > t1 ? t * t * t : t2 * (t - t0); } function lrgb2rgb(x) { return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055); } function rgb2lrgb(x) { return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4); } function hclConvert(o) { if (o instanceof Hcl) return new Hcl(o.h, o.c, o.l, o.opacity); if (!(o instanceof Lab)) o = labConvert(o); if (o.a === 0 && o.b === 0) return new Hcl(NaN, 0 < o.l && o.l < 100 ? 0 : NaN, o.l, o.opacity); var h = Math.atan2(o.b, o.a) * rad2deg; return new Hcl(h < 0 ? h + 360 : h, Math.sqrt(o.a * o.a + o.b * o.b), o.l, o.opacity); } function hcl(h, c, l, opacity) { return arguments.length === 1 ? hclConvert(h) : new Hcl(h, c, l, opacity == null ? 1 : opacity); } function Hcl(h, c, l, opacity) { this.h = +h; this.c = +c; this.l = +l; this.opacity = +opacity; } function hcl2lab(o) { if (isNaN(o.h)) return new Lab(o.l, 0, 0, o.opacity); var h = o.h * deg2rad; return new Lab(o.l, Math.cos(h) * o.c, Math.sin(h) * o.c, o.opacity); } define(Hcl, hcl, extend(Color, { brighter: function(k) { return new Hcl(this.h, this.c, this.l + K * (k == null ? 1 : k), this.opacity); }, darker: function(k) { return new Hcl(this.h, this.c, this.l - K * (k == null ? 1 : k), this.opacity); }, rgb: function() { return hcl2lab(this).rgb(); } })); function constant$3(x) { return function() { return x; }; } function linear(a, d) { return function(t) { return a + t * d; }; } function exponential(a, b, y) { return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) { return Math.pow(a + t * b, y); }; } function gamma(y) { return (y = +y) === 1 ? nogamma : function(a, b) { return b - a ? exponential(a, b, y) : constant$3(isNaN(a) ? b : a); }; } function nogamma(a, b) { var d = b - a; return d ? linear(a, d) : constant$3(isNaN(a) ? b : a); } var interpolateRgb = (function rgbGamma(y) { var color = gamma(y); function rgb$1(start, end) { var r = color((start = rgb(start)).r, (end = rgb(end)).r), g = color(start.g, end.g), b = color(start.b, end.b), opacity = nogamma(start.opacity, end.opacity); return function(t) { start.r = r(t); start.g = g(t); start.b = b(t); start.opacity = opacity(t); return start + ""; }; } rgb$1.gamma = rgbGamma; return rgb$1; })(1); function numberArray(a, b) { if (!b) b = []; var n = a ? Math.min(b.length, a.length) : 0, c = b.slice(), i; return function(t) { for (i = 0; i < n; ++i) c[i] = a[i] * (1 - t) + b[i] * t; return c; }; } function isNumberArray(x) { return ArrayBuffer.isView(x) && !(x instanceof DataView); } function genericArray(a, b) { var nb = b ? b.length : 0, na = a ? Math.min(nb, a.length) : 0, x = new Array(na), c = new Array(nb), i; for (i = 0; i < na; ++i) x[i] = interpolate(a[i], b[i]); for (; i < nb; ++i) c[i] = b[i]; return function(t) { for (i = 0; i < na; ++i) c[i] = x[i](t); return c; }; } function date$1(a, b) { var d = new Date; return a = +a, b = +b, function(t) { return d.setTime(a * (1 - t) + b * t), d; }; } function interpolateNumber(a, b) { return a = +a, b = +b, function(t) { return a * (1 - t) + b * t; }; } function object$1(a, b) { var i = {}, c = {}, k; if (a === null || typeof a !== "object") a = {}; if (b === null || typeof b !== "object") b = {}; for (k in b) { if (k in a) { i[k] = interpolate(a[k], b[k]); } else { c[k] = b[k]; } } return function(t) { for (k in i) c[k] = i[k](t); return c; }; } var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, reB = new RegExp(reA.source, "g"); function zero(b) { return function() { return b; }; } function one(b) { return function(t) { return b(t) + ""; }; } function interpolateString(a, b) { var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b am, // current match in a bm, // current match in b bs, // string preceding current number in b, if any i = -1, // index in s s = [], // string constants and placeholders q = []; // number interpolators // Coerce inputs to strings. a = a + "", b = b + ""; // Interpolate pairs of numbers in a & b. while ((am = reA.exec(a)) && (bm = reB.exec(b))) { if ((bs = bm.index) > bi) { // a string precedes the next number in b bs = b.slice(bi, bs); if (s[i]) s[i] += bs; // coalesce with previous string else s[++i] = bs; } if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match if (s[i]) s[i] += bm; // coalesce with previous string else s[++i] = bm; } else { // interpolate non-matching numbers s[++i] = null; q.push({i: i, x: interpolateNumber(am, bm)}); } bi = reB.lastIndex; } // Add remains of b. if (bi < b.length) { bs = b.slice(bi); if (s[i]) s[i] += bs; // coalesce with previous string else s[++i] = bs; } // Special optimization for only a single match. // Otherwise, interpolate each of the numbers and rejoin the string. return s.length < 2 ? (q[0] ? one(q[0].x) : zero(b)) : (b = q.length, function(t) { for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t); return s.join(""); }); } function interpolate(a, b) { var t = typeof b, c; return b == null || t === "boolean" ? constant$3(b) : (t === "number" ? interpolateNumber : t === "string" ? ((c = color(b)) ? (b = c, interpolateRgb) : interpolateString) : b instanceof color ? interpolateRgb : b instanceof Date ? date$1 : isNumberArray(b) ? numberArray : Array.isArray(b) ? genericArray : typeof b.valueOf !== "function" && typeof b.toString !== "function" || isNaN(b) ? object$1 : interpolateNumber)(a, b); } function interpolateRound(a, b) { return a = +a, b = +b, function(t) { return Math.round(a * (1 - t) + b * t); }; } var degrees = 180 / Math.PI; var identity$1 = { translateX: 0, translateY: 0, rotate: 0, skewX: 0, scaleX: 1, scaleY: 1 }; function decompose(a, b, c, d, e, f) { var scaleX, scaleY, skewX; if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX; if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX; if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY; if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX; return { translateX: e, translateY: f, rotate: Math.atan2(b, a) * degrees, skewX: Math.atan(skewX) * degrees, scaleX: scaleX, scaleY: scaleY }; } var cssNode, cssRoot, cssView, svgNode; function parseCss(value) { if (value === "none") return identity$1; if (!cssNode) cssNode = document.createElement("DIV"), cssRoot = document.documentElement, cssView = document.defaultView; cssNode.style.transform = value; value = cssView.getComputedStyle(cssRoot.appendChild(cssNode), null).getPropertyValue("transform"); cssRoot.removeChild(cssNode); value = value.slice(7, -1).split(","); return decompose(+value[0], +value[1], +value[2], +value[3], +value[4], +value[5]); } function parseSvg(value) { if (value == null) return identity$1; if (!svgNode) svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g"); svgNode.setAttribute("transform", value); if (!(value = svgNode.transform.baseVal.consolidate())) return identity$1; value = value.matrix; return decompose(value.a, value.b, value.c, value.d, value.e, value.f); } function interpolateTransform(parse, pxComma, pxParen, degParen) { function pop(s) { return s.length ? s.pop() + " " : ""; } function translate(xa, ya, xb, yb, s, q) { if (xa !== xb || ya !== yb) { var i = s.push("translate(", null, pxComma, null, pxParen); q.push({i: i - 4, x: interpolateNumber(xa, xb)}, {i: i - 2, x: interpolateNumber(ya, yb)}); } else if (xb || yb) { s.push("translate(" + xb + pxComma + yb + pxParen); } } function rotate(a, b, s, q) { if (a !== b) { if (a - b > 180) b += 360; else if (b - a > 180) a += 360; // shortest path q.push({i: s.push(pop(s) + "rotate(", null, degParen) - 2, x: interpolateNumber(a, b)}); } else if (b) { s.push(pop(s) + "rotate(" + b + degParen); } } function skewX(a, b, s, q) { if (a !== b) { q.push({i: s.push(pop(s) + "skewX(", null, degParen) - 2, x: interpolateNumber(a, b)}); } else if (b) { s.push(pop(s) + "skewX(" + b + degParen); } } function scale(xa, ya, xb, yb, s, q) { if (xa !== xb || ya !== yb) { var i = s.push(pop(s) + "scale(", null, ",", null, ")"); q.push({i: i - 4, x: interpolateNumber(xa, xb)}, {i: i - 2, x: interpolateNumber(ya, yb)}); } else if (xb !== 1 || yb !== 1) { s.push(pop(s) + "scale(" + xb + "," + yb + ")"); } } return function(a, b) { var s = [], // string constants and placeholders q = []; // number interpolators a = parse(a), b = parse(b); translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q); rotate(a.rotate, b.rotate, s, q); skewX(a.skewX, b.skewX, s, q); scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q); a = b = null; // gc return function(t) { var i = -1, n = q.length, o; while (++i < n) s[(o = q[i]).i] = o.x(t); return s.join(""); }; }; } var interpolateTransformCss = interpolateTransform(parseCss, "px, ", "px)", "deg)"); var interpolateTransformSvg = interpolateTransform(parseSvg, ", ", ")", ")"); function tweenRemove(id, name) { var tween0, tween1; return function() { var schedule = set$1(this, id), tween = schedule.tween; // If this node shared tween with the previous node, // just assign the updated shared tween and we’re done! // Otherwise, copy-on-write. if (tween !== tween0) { tween1 = tween0 = tween; for (var i = 0, n = tween1.length; i < n; ++i) { if (tween1[i].name === name) { tween1 = tween1.slice(); tween1.splice(i, 1); break; } } } schedule.tween = tween1; }; } function tweenFunction(id, name, value) { var tween0, tween1; if (typeof value !== "function") throw new Error; return function() { var schedule = set$1(this, id), tween = schedule.tween; // If this node shared tween with the previous node, // just assign the updated shared tween and we’re done! // Otherwise, copy-on-write. if (tween !== tween0) { tween1 = (tween0 = tween).slice(); for (var t = {name: name, value: value}, i = 0, n = tween1.length; i < n; ++i) { if (tween1[i].name === name) { tween1[i] = t; break; } } if (i === n) tween1.push(t); } schedule.tween = tween1; }; } function transition_tween(name, value) { var id = this._id; name += ""; if (arguments.length < 2) { var tween = get$1(this.node(), id).tween; for (var i = 0, n = tween.length, t; i < n; ++i) { if ((t = tween[i]).name === name) { return t.value; } } return null; } return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value)); } function tweenValue(transition, name, value) { var id = transition._id; transition.each(function() { var schedule = set$1(this, id); (schedule.value || (schedule.value = {}))[name] = value.apply(this, arguments); }); return function(node) { return get$1(node, id).value[name]; }; } function interpolate$1(a, b) { var c; return (typeof b === "number" ? interpolateNumber : b instanceof color ? interpolateRgb : (c = color(b)) ? (b = c, interpolateRgb) : interpolateString)(a, b); } function attrRemove$1(name) { return function() { this.removeAttribute(name); }; } function attrRemoveNS$1(fullname) { return function() { this.removeAttributeNS(fullname.space, fullname.local); }; } function attrConstant$1(name, interpolate, value1) { var string00, string1 = value1 + "", interpolate0; return function() { var string0 = this.getAttribute(name); return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1); }; } function attrConstantNS$1(fullname, interpolate, value1) { var string00, string1 = value1 + "", interpolate0; return function() { var string0 = this.getAttributeNS(fullname.space, fullname.local); return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1); }; } function attrFunction$1(name, interpolate, value) { var string00, string10, interpolate0; return function() { var string0, value1 = value(this), string1; if (value1 == null) return void this.removeAttribute(name); string0 = this.getAttribute(name); string1 = value1 + ""; return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1)); }; } function attrFunctionNS$1(fullname, interpolate, value) { var string00, string10, interpolate0; return function() { var string0, value1 = value(this), string1; if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local); string0 = this.getAttributeNS(fullname.space, fullname.local); string1 = value1 + ""; return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1)); }; } function transition_attr(name, value) { var fullname = namespace(name), i = fullname === "transform" ? interpolateTransformSvg : interpolate$1; return this.attrTween(name, typeof value === "function" ? (fullname.local ? attrFunctionNS$1 : attrFunction$1)(fullname, i, tweenValue(this, "attr." + name, value)) : value == null ? (fullname.local ? attrRemoveNS$1 : attrRemove$1)(fullname) : (fullname.local ? attrConstantNS$1 : attrConstant$1)(fullname, i, value)); } function attrInterpolate(name, i) { return function(t) { this.setAttribute(name, i.call(this, t)); }; } function attrInterpolateNS(fullname, i) { return function(t) { this.setAttributeNS(fullname.space, fullname.local, i.call(this, t)); }; } function attrTweenNS(fullname, value) { var t0, i0; function tween() { var i = value.apply(this, arguments); if (i !== i0) t0 = (i0 = i) && attrInterpolateNS(fullname, i); return t0; } tween._value = value; return tween; } function attrTween(name, value) { var t0, i0; function tween() { var i = value.apply(this, arguments); if (i !== i0) t0 = (i0 = i) && attrInterpolate(name, i); return t0; } tween._value = value; return tween; } function transition_attrTween(name, value) { var key = "attr." + name; if (arguments.length < 2) return (key = this.tween(key)) && key._value; if (value == null) return this.tween(key, null); if (typeof value !== "function") throw new Error; var fullname = namespace(name); return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value)); } function delayFunction(id, value) { return function() { init$1(this, id).delay = +value.apply(this, arguments); }; } function delayConstant(id, value) { return value = +value, function() { init$1(this, id).delay = value; }; } function transition_delay(value) { var id = this._id; return arguments.length ? this.each((typeof value === "function" ? delayFunction : delayConstant)(id, value)) : get$1(this.node(), id).delay; } function durationFunction(id, value) { return function() { set$1(this, id).duration = +value.apply(this, arguments); }; } function durationConstant(id, value) { return value = +value, function() { set$1(this, id).duration = value; }; } function transition_duration(value) { var id = this._id; return arguments.length ? this.each((typeof value === "function" ? durationFunction : durationConstant)(id, value)) : get$1(this.node(), id).duration; } function easeConstant(id, value) { if (typeof value !== "function") throw new Error; return function() { set$1(this, id).ease = value; }; } function transition_ease(value) { var id = this._id; return arguments.length ? this.each(easeConstant(id, value)) : get$1(this.node(), id).ease; } function transition_filter(match) { if (typeof match !== "function") match = matcher(match); for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) { if ((node = group[i]) && match.call(node, node.__data__, i, group)) { subgroup.push(node); } } } return new Transition(subgroups, this._parents, this._name, this._id); } function transition_merge(transition) { if (transition._id !== this._id) throw new Error; for (var groups0 = this._groups, groups1 = transition._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) { for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) { if (node = group0[i] || group1[i]) { merge[i] = node; } } } for (; j < m0; ++j) { merges[j] = groups0[j]; } return new Transition(merges, this._parents, this._name, this._id); } function start(name) { return (name + "").trim().split(/^|\s+/).every(function(t) { var i = t.indexOf("."); if (i >= 0) t = t.slice(0, i); return !t || t === "start"; }); } function onFunction(id, name, listener) { var on0, on1, sit = start(name) ? init$1 : set$1; return function() { var schedule = sit(this, id), on = schedule.on; // If this node shared a dispatch with the previous node, // just assign the updated shared dispatch and we’re done! // Otherwise, copy-on-write. if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener); schedule.on = on1; }; } function transition_on(name, listener) { var id = this._id; return arguments.length < 2 ? get$1(this.node(), id).on.on(name) : this.each(onFunction(id, name, listener)); } function removeFunction(id) { return function() { var parent = this.parentNode; for (var i in this.__transition) if (+i !== id) return; if (parent) parent.removeChild(this); }; } function transition_remove() { return this.on("end.remove", removeFunction(this._id)); } function transition_select(select) { var name = this._name, id = this._id; if (typeof select !== "function") select = selector(select); for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) { if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) { if ("__data__" in node) subnode.__data__ = node.__data__; subgroup[i] = subnode; schedule(subgroup[i], name, id, i, subgroup, get$1(node, id)); } } } return new Transition(subgroups, this._parents, name, id); } function transition_selectAll(select) { var name = this._name, id = this._id; if (typeof select !== "function") select = selectorAll(select); for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) { for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { if (node = group[i]) { for (var children = select.call(node, node.__data__, i, group), child, inherit = get$1(node, id), k = 0, l = children.length; k < l; ++k) { if (child = children[k]) { schedule(child, name, id, k, children, inherit); } } subgroups.push(children); parents.push(node); } } } return new Transition(subgroups, parents, name, id); } var Selection$1 = selection.prototype.constructor; function transition_selection() { return new Selection$1(this._groups, this._parents); } function styleNull(name, interpolate) { var string00, string10, interpolate0; return function() { var string0 = styleValue(this, name), string1 = (this.style.removeProperty(name), styleValue(this, name)); return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : interpolate0 = interpolate(string00 = string0, string10 = string1); }; } function styleRemove$1(name) { return function() { this.style.removeProperty(name); }; } function styleConstant$1(name, interpolate, value1) { var string00, string1 = value1 + "", interpolate0; return function() { var string0 = styleValue(this, name); return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1); }; } function styleFunction$1(name, interpolate, value) { var string00, string10, interpolate0; return function() { var string0 = styleValue(this, name), value1 = value(this), string1 = value1 + ""; if (value1 == null) string1 = value1 = (this.style.removeProperty(name), styleValue(this, name)); return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1)); }; } function styleMaybeRemove(id, name) { var on0, on1, listener0, key = "style." + name, event = "end." + key, remove; return function() { var schedule = set$1(this, id), on = schedule.on, listener = schedule.value[key] == null ? remove || (remove = styleRemove$1(name)) : undefined; // If this node shared a dispatch with the previous node, // just assign the updated shared dispatch and we’re done! // Otherwise, copy-on-write. if (on !== on0 || listener0 !== listener) (on1 = (on0 = on).copy()).on(event, listener0 = listener); schedule.on = on1; }; } function transition_style(name, value, priority) { var i = (name += "") === "transform" ? interpolateTransformCss : interpolate$1; return value == null ? this .styleTween(name, styleNull(name, i)) .on("end.style." + name, styleRemove$1(name)) : typeof value === "function" ? this .styleTween(name, styleFunction$1(name, i, tweenValue(this, "style." + name, value))) .each(styleMaybeRemove(this._id, name)) : this .styleTween(name, styleConstant$1(name, i, value), priority) .on("end.style." + name, null); } function styleInterpolate(name, i, priority) { return function(t) { this.style.setProperty(name, i.call(this, t), priority); }; } function styleTween(name, value, priority) { var t, i0; function tween() { var i = value.apply(this, arguments); if (i !== i0) t = (i0 = i) && styleInterpolate(name, i, priority); return t; } tween._value = value; return tween; } function transition_styleTween(name, value, priority) { var key = "style." + (name += ""); if (arguments.length < 2) return (key = this.tween(key)) && key._value; if (value == null) return this.tween(key, null); if (typeof value !== "function") throw new Error; return this.tween(key, styleTween(name, value, priority == null ? "" : priority)); } function textConstant$1(value) { return function() { this.textContent = value; }; } function textFunction$1(value) { return function() { var value1 = value(this); this.textContent = value1 == null ? "" : value1; }; } function transition_text(value) { return this.tween("text", typeof value === "function" ? textFunction$1(tweenValue(this, "text", value)) : textConstant$1(value == null ? "" : value + "")); } function textInterpolate(i) { return function(t) { this.textContent = i.call(this, t); }; } function textTween(value) { var t0, i0; function tween() { var i = value.apply(this, arguments); if (i !== i0) t0 = (i0 = i) && textInterpolate(i); return t0; } tween._value = value; return tween; } function transition_textTween(value) { var key = "text"; if (arguments.length < 1) return (key = this.tween(key)) && key._value; if (value == null) return this.tween(key, null); if (typeof value !== "function") throw new Error; return this.tween(key, textTween(value)); } function transition_transition() { var name = this._name, id0 = this._id, id1 = newId(); for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) { for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { if (node = group[i]) { var inherit = get$1(node, id0); schedule(node, name, id1, i, group, { time: inherit.time + inherit.delay + inherit.duration, delay: 0, duration: inherit.duration, ease: inherit.ease }); } } } return new Transition(groups, this._parents, name, id1); } function transition_end() { var on0, on1, that = this, id = that._id, size = that.size(); return new Promise(function(resolve, reject) { var cancel = {value: reject}, end = {value: function() { if (--size === 0) resolve(); }}; that.each(function() { var schedule = set$1(this, id), on = schedule.on; // If this node shared a dispatch with the previous node, // just assign the updated shared dispatch and we’re done! // Otherwise, copy-on-write. if (on !== on0) { on1 = (on0 = on).copy(); on1._.cancel.push(cancel); on1._.interrupt.push(cancel); on1._.end.push(end); } schedule.on = on1; }); }); } var id = 0; function Transition(groups, parents, name, id) { this._groups = groups; this._parents = parents; this._name = name; this._id = id; } function transition(name) { return selection().transition(name); } function newId() { return ++id; } var selection_prototype = selection.prototype; Transition.prototype = transition.prototype = { constructor: Transition, select: transition_select, selectAll: transition_selectAll, filter: transition_filter, merge: transition_merge, selection: transition_selection, transition: transition_transition, call: selection_prototype.call, nodes: selection_prototype.nodes, node: selection_prototype.node, size: selection_prototype.size, empty: selection_prototype.empty, each: selection_prototype.each, on: transition_on, attr: transition_attr, attrTween: transition_attrTween, style: transition_style, styleTween: transition_styleTween, text: transition_text, textTween: transition_textTween, remove: transition_remove, tween: transition_tween, delay: transition_delay, duration: transition_duration, ease: transition_ease, end: transition_end }; function cubicInOut(t) { return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2; } var defaultTiming = { time: null, // Set on use. delay: 0, duration: 250, ease: cubicInOut }; function inherit(node, id) { var timing; while (!(timing = node.__transition) || !(timing = timing[id])) { if (!(node = node.parentNode)) { return defaultTiming.time = now(), defaultTiming; } } return timing; } function selection_transition(name) { var id, timing; if (name instanceof Transition) { id = name._id, name = name._name; } else { id = newId(), (timing = defaultTiming).time = now(), name = name == null ? null : name + ""; } for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) { for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { if (node = group[i]) { schedule(node, name, id, i, group, timing || inherit(node, id)); } } } return new Transition(groups, this._parents, name, id); } selection.prototype.interrupt = selection_interrupt; selection.prototype.transition = selection_transition; // Computes the decimal coefficient and exponent of the specified number x with // significant digits p, where x is positive and p is in [1, 21] or undefined. // For example, formatDecimal(1.23) returns ["123", 0]. function formatDecimal(x, p) { if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity var i, coefficient = x.slice(0, i); // The string returned by toExponential either has the form \d\.\d+e[-+]\d+ // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3). return [ coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient, +x.slice(i + 1) ]; } function exponent(x) { return x = formatDecimal(Math.abs(x)), x ? x[1] : NaN; } function formatGroup(grouping, thousands) { return function(value, width) { var i = value.length, t = [], j = 0, g = grouping[0], length = 0; while (i > 0 && g > 0) { if (length + g + 1 > width) g = Math.max(1, width - length); t.push(value.substring(i -= g, i + g)); if ((length += g + 1) > width) break; g = grouping[j = (j + 1) % grouping.length]; } return t.reverse().join(thousands); }; } function formatNumerals(numerals) { return function(value) { return value.replace(/[0-9]/g, function(i) { return numerals[+i]; }); }; } // [[fill]align][sign][symbol][0][width][,][.precision][~][type] var re = /^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i; function formatSpecifier(specifier) { if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier); var match; return new FormatSpecifier({ fill: match[1], align: match[2], sign: match[3], symbol: match[4], zero: match[5], width: match[6], comma: match[7], precision: match[8] && match[8].slice(1), trim: match[9], type: match[10] }); } formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof function FormatSpecifier(specifier) { this.fill = specifier.fill === undefined ? " " : specifier.fill + ""; this.align = specifier.align === undefined ? ">" : specifier.align + ""; this.sign = specifier.sign === undefined ? "-" : specifier.sign + ""; this.symbol = specifier.symbol === undefined ? "" : specifier.symbol + ""; this.zero = !!specifier.zero; this.width = specifier.width === undefined ? undefined : +specifier.width; this.comma = !!specifier.comma; this.precision = specifier.precision === undefined ? undefined : +specifier.precision; this.trim = !!specifier.trim; this.type = specifier.type === undefined ? "" : specifier.type + ""; } FormatSpecifier.prototype.toString = function() { return this.fill + this.align + this.sign + this.symbol + (this.zero ? "0" : "") + (this.width === undefined ? "" : Math.max(1, this.width | 0)) + (this.comma ? "," : "") + (this.precision === undefined ? "" : "." + Math.max(0, this.precision | 0)) + (this.trim ? "~" : "") + this.type; }; // Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k. function formatTrim(s) { out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) { switch (s[i]) { case ".": i0 = i1 = i; break; case "0": if (i0 === 0) i0 = i; i1 = i; break; default: if (!+s[i]) break out; if (i0 > 0) i0 = 0; break; } } return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s; } var prefixExponent; function formatPrefixAuto(x, p) { var d = formatDecimal(x, p); if (!d) return x + ""; var coefficient = d[0], exponent = d[1], i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1, n = coefficient.length; return i === n ? coefficient : i > n ? coefficient + new Array(i - n + 1).join("0") : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i) : "0." + new Array(1 - i).join("0") + formatDecimal(x, Math.max(0, p + i - 1))[0]; // less than 1y! } function formatRounded(x, p) { var d = formatDecimal(x, p); if (!d) return x + ""; var coefficient = d[0], exponent = d[1]; return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1) : coefficient + new Array(exponent - coefficient.length + 2).join("0"); } var formatTypes = { "%": function(x, p) { return (x * 100).toFixed(p); }, "b": function(x) { return Math.round(x).toString(2); }, "c": function(x) { return x + ""; }, "d": function(x) { return Math.round(x).toString(10); }, "e": function(x, p) { return x.toExponential(p); }, "f": function(x, p) { return x.toFixed(p); }, "g": function(x, p) { return x.toPrecision(p); }, "o": function(x) { return Math.round(x).toString(8); }, "p": function(x, p) { return formatRounded(x * 100, p); }, "r": formatRounded, "s": formatPrefixAuto, "X": function(x) { return Math.round(x).toString(16).toUpperCase(); }, "x": function(x) { return Math.round(x).toString(16); } }; function identity$2(x) { return x; } var map = Array.prototype.map, prefixes = ["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"]; function formatLocale(locale) { var group = locale.grouping === undefined || locale.thousands === undefined ? identity$2 : formatGroup(map.call(locale.grouping, Number), locale.thousands + ""), currencyPrefix = locale.currency === undefined ? "" : locale.currency[0] + "", currencySuffix = locale.currency === undefined ? "" : locale.currency[1] + "", decimal = locale.decimal === undefined ? "." : locale.decimal + "", numerals = locale.numerals === undefined ? identity$2 : formatNumerals(map.call(locale.numerals, String)), percent = locale.percent === undefined ? "%" : locale.percent + "", minus = locale.minus === undefined ? "-" : locale.minus + "", nan = locale.nan === undefined ? "NaN" : locale.nan + ""; function newFormat(specifier) { specifier = formatSpecifier(specifier); var fill = specifier.fill, align = specifier.align, sign = specifier.sign, symbol = specifier.symbol, zero = specifier.zero, width = specifier.width, comma = specifier.comma, precision = specifier.precision, trim = specifier.trim, type = specifier.type; // The "n" type is an alias for ",g". if (type === "n") comma = true, type = "g"; // The "" type, and any invalid type, is an alias for ".12~g". else if (!formatTypes[type]) precision === undefined && (precision = 12), trim = true, type = "g"; // If zero fill is specified, padding goes after sign and before digits. if (zero || (fill === "0" && align === "=")) zero = true, fill = "0", align = "="; // Compute the prefix and suffix. // For SI-prefix, the suffix is lazily computed. var prefix = symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "", suffix = symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : ""; // What format function should we use? // Is this an integer type? // Can this type generate exponential notation? var formatType = formatTypes[type], maybeSuffix = /[defgprs%]/.test(type); // Set the default precision if not specified, // or clamp the specified precision to the supported range. // For significant precision, it must be in [1, 21]. // For fixed precision, it must be in [0, 20]. precision = precision === undefined ? 6 : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision)) : Math.max(0, Math.min(20, precision)); function format(value) { var valuePrefix = prefix, valueSuffix = suffix, i, n, c; if (type === "c") { valueSuffix = formatType(value) + valueSuffix; value = ""; } else { value = +value; // Perform the initial formatting. var valueNegative = value < 0; value = isNaN(value) ? nan : formatType(Math.abs(value), precision); // Trim insignificant zeros. if (trim) value = formatTrim(value); // If a negative value rounds to zero during formatting, treat as positive. if (valueNegative && +value === 0) valueNegative = false; // Compute the prefix and suffix. valuePrefix = (valueNegative ? (sign === "(" ? sign : minus) : sign === "-" || sign === "(" ? "" : sign) + valuePrefix; valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : ""); // Break the formatted value into the integer “value” part that can be // grouped, and fractional or exponential “suffix” part that is not. if (maybeSuffix) { i = -1, n = value.length; while (++i < n) { if (c = value.charCodeAt(i), 48 > c || c > 57) { valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix; value = value.slice(0, i); break; } } } } // If the fill character is not "0", grouping is applied before padding. if (comma && !zero) value = group(value, Infinity); // Compute the padding. var length = valuePrefix.length + value.length + valueSuffix.length, padding = length < width ? new Array(width - length + 1).join(fill) : ""; // If the fill character is "0", grouping is applied after padding. if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = ""; // Reconstruct the final output based on the desired alignment. switch (align) { case "<": value = valuePrefix + value + valueSuffix + padding; break; case "=": value = valuePrefix + padding + value + valueSuffix; break; case "^": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break; default: value = padding + valuePrefix + value + valueSuffix; break; } return numerals(value); } format.toString = function() { return specifier + ""; }; return format; } function formatPrefix(specifier, value) { var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)), e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3, k = Math.pow(10, -e), prefix = prefixes[8 + e / 3]; return function(value) { return f(k * value) + prefix; }; } return { format: newFormat, formatPrefix: formatPrefix }; } var locale; var format; var formatPrefix; defaultLocale({ decimal: ".", thousands: ",", grouping: [3], currency: ["$", ""], minus: "-" }); function defaultLocale(definition) { locale = formatLocale(definition); format = locale.format; formatPrefix = locale.formatPrefix; return locale; } function precisionFixed(step) { return Math.max(0, -exponent(Math.abs(step))); } function precisionPrefix(step, value) { return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step))); } function precisionRound(step, max) { step = Math.abs(step), max = Math.abs(max) - step; return Math.max(0, exponent(max) - exponent(step)) + 1; } var t0$1 = new Date, t1$1 = new Date; function newInterval(floori, offseti, count, field) { function interval(date) { return floori(date = arguments.length === 0 ? new Date : new Date(+date)), date; } interval.floor = function(date) { return floori(date = new Date(+date)), date; }; interval.ceil = function(date) { return floori(date = new Date(date - 1)), offseti(date, 1), floori(date), date; }; interval.round = function(date) { var d0 = interval(date), d1 = interval.ceil(date); return date - d0 < d1 - date ? d0 : d1; }; interval.offset = function(date, step) { return offseti(date = new Date(+date), step == null ? 1 : Math.floor(step)), date; }; interval.range = function(start, stop, step) { var range = [], previous; start = interval.ceil(start); step = step == null ? 1 : Math.floor(step); if (!(start < stop) || !(step > 0)) return range; // also handles Invalid Date do range.push(previous = new Date(+start)), offseti(start, step), floori(start); while (previous < start && start < stop); return range; }; interval.filter = function(test) { return newInterval(function(date) { if (date >= date) while (floori(date), !test(date)) date.setTime(date - 1); }, function(date, step) { if (date >= date) { if (step < 0) while (++step <= 0) { while (offseti(date, -1), !test(date)) {} // eslint-disable-line no-empty } else while (--step >= 0) { while (offseti(date, +1), !test(date)) {} // eslint-disable-line no-empty } } }); }; if (count) { interval.count = function(start, end) { t0$1.setTime(+start), t1$1.setTime(+end); floori(t0$1), floori(t1$1); return Math.floor(count(t0$1, t1$1)); }; interval.every = function(step) { step = Math.floor(step); return !isFinite(step) || !(step > 0) ? null : !(step > 1) ? interval : interval.filter(field ? function(d) { return field(d) % step === 0; } : function(d) { return interval.count(0, d) % step === 0; }); }; } return interval; } var millisecond = newInterval(function() { // noop }, function(date, step) { date.setTime(+date + step); }, function(start, end) { return end - start; }); // An optimized implementation for this simple case. millisecond.every = function(k) { k = Math.floor(k); if (!isFinite(k) || !(k > 0)) return null; if (!(k > 1)) return millisecond; return newInterval(function(date) { date.setTime(Math.floor(date / k) * k); }, function(date, step) { date.setTime(+date + step * k); }, function(start, end) { return (end - start) / k; }); }; var durationSecond = 1e3; var durationMinute = 6e4; var durationHour = 36e5; var durationDay = 864e5; var durationWeek = 6048e5; var second = newInterval(function(date) { date.setTime(date - date.getMilliseconds()); }, function(date, step) { date.setTime(+date + step * durationSecond); }, function(start, end) { return (end - start) / durationSecond; }, function(date) { return date.getUTCSeconds(); }); var day = newInterval(function(date) { date.setHours(0, 0, 0, 0); }, function(date, step) { date.setDate(date.getDate() + step); }, function(start, end) { return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationDay; }, function(date) { return date.getDate() - 1; }); function weekday(i) { return newInterval(function(date) { date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7); date.setHours(0, 0, 0, 0); }, function(date, step) { date.setDate(date.getDate() + step * 7); }, function(start, end) { return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationWeek; }); } var sunday = weekday(0); var monday = weekday(1); var tuesday = weekday(2); var wednesday = weekday(3); var thursday = weekday(4); var friday = weekday(5); var saturday = weekday(6); var year = newInterval(function(date) { date.setMonth(0, 1); date.setHours(0, 0, 0, 0); }, function(date, step) { date.setFullYear(date.getFullYear() + step); }, function(start, end) { return end.getFullYear() - start.getFullYear(); }, function(date) { return date.getFullYear(); }); // An optimized implementation for this simple case. year.every = function(k) { return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) { date.setFullYear(Math.floor(date.getFullYear() / k) * k); date.setMonth(0, 1); date.setHours(0, 0, 0, 0); }, function(date, step) { date.setFullYear(date.getFullYear() + step * k); }); }; var utcMinute = newInterval(function(date) { date.setUTCSeconds(0, 0); }, function(date, step) { date.setTime(+date + step * durationMinute); }, function(start, end) { return (end - start) / durationMinute; }, function(date) { return date.getUTCMinutes(); }); var utcHour = newInterval(function(date) { date.setUTCMinutes(0, 0, 0); }, function(date, step) { date.setTime(+date + step * durationHour); }, function(start, end) { return (end - start) / durationHour; }, function(date) { return date.getUTCHours(); }); var utcDay = newInterval(function(date) { date.setUTCHours(0, 0, 0, 0); }, function(date, step) { date.setUTCDate(date.getUTCDate() + step); }, function(start, end) { return (end - start) / durationDay; }, function(date) { return date.getUTCDate() - 1; }); function utcWeekday(i) { return newInterval(function(date) { date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7); date.setUTCHours(0, 0, 0, 0); }, function(date, step) { date.setUTCDate(date.getUTCDate() + step * 7); }, function(start, end) { return (end - start) / durationWeek; }); } var utcSunday = utcWeekday(0); var utcMonday = utcWeekday(1); var utcTuesday = utcWeekday(2); var utcWednesday = utcWeekday(3); var utcThursday = utcWeekday(4); var utcFriday = utcWeekday(5); var utcSaturday = utcWeekday(6); var utcMonth = newInterval(function(date) { date.setUTCDate(1); date.setUTCHours(0, 0, 0, 0); }, function(date, step) { date.setUTCMonth(date.getUTCMonth() + step); }, function(start, end) { return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12; }, function(date) { return date.getUTCMonth(); }); var utcYear = newInterval(function(date) { date.setUTCMonth(0, 1); date.setUTCHours(0, 0, 0, 0); }, function(date, step) { date.setUTCFullYear(date.getUTCFullYear() + step); }, function(start, end) { return end.getUTCFullYear() - start.getUTCFullYear(); }, function(date) { return date.getUTCFullYear(); }); // An optimized implementation for this simple case. utcYear.every = function(k) { return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) { date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k); date.setUTCMonth(0, 1); date.setUTCHours(0, 0, 0, 0); }, function(date, step) { date.setUTCFullYear(date.getUTCFullYear() + step * k); }); }; function localDate(d) { if (0 <= d.y && d.y < 100) { var date = new Date(-1, d.m, d.d, d.H, d.M, d.S, d.L); date.setFullYear(d.y); return date; } return new Date(d.y, d.m, d.d, d.H, d.M, d.S, d.L); } function utcDate(d) { if (0 <= d.y && d.y < 100) { var date = new Date(Date.UTC(-1, d.m, d.d, d.H, d.M, d.S, d.L)); date.setUTCFullYear(d.y); return date; } return new Date(Date.UTC(d.y, d.m, d.d, d.H, d.M, d.S, d.L)); } function newDate(y, m, d) { return {y: y, m: m, d: d, H: 0, M: 0, S: 0, L: 0}; } function formatLocale$1(locale) { var locale_dateTime = locale.dateTime, locale_date = locale.date, locale_time = locale.time, locale_periods = locale.periods, locale_weekdays = locale.days, locale_shortWeekdays = locale.shortDays, locale_months = locale.months, locale_shortMonths = locale.shortMonths; var periodRe = formatRe(locale_periods), periodLookup = formatLookup(locale_periods), weekdayRe = formatRe(locale_weekdays), weekdayLookup = formatLookup(locale_weekdays), shortWeekdayRe = formatRe(locale_shortWeekdays), shortWeekdayLookup = formatLookup(locale_shortWeekdays), monthRe = formatRe(locale_months), monthLookup = formatLookup(locale_months), shortMonthRe = formatRe(locale_shortMonths), shortMonthLookup = formatLookup(locale_shortMonths); var formats = { "a": formatShortWeekday, "A": formatWeekday, "b": formatShortMonth, "B": formatMonth, "c": null, "d": formatDayOfMonth, "e": formatDayOfMonth, "f": formatMicroseconds, "H": formatHour24, "I": formatHour12, "j": formatDayOfYear, "L": formatMilliseconds, "m": formatMonthNumber, "M": formatMinutes, "p": formatPeriod, "q": formatQuarter, "Q": formatUnixTimestamp, "s": formatUnixTimestampSeconds, "S": formatSeconds, "u": formatWeekdayNumberMonday, "U": formatWeekNumberSunday, "V": formatWeekNumberISO, "w": formatWeekdayNumberSunday, "W": formatWeekNumberMonday, "x": null, "X": null, "y": formatYear, "Y": formatFullYear, "Z": formatZone, "%": formatLiteralPercent }; var utcFormats = { "a": formatUTCShortWeekday, "A": formatUTCWeekday, "b": formatUTCShortMonth, "B": formatUTCMonth, "c": null, "d": formatUTCDayOfMonth, "e": formatUTCDayOfMonth, "f": formatUTCMicroseconds, "H": formatUTCHour24, "I": formatUTCHour12, "j": formatUTCDayOfYear, "L": formatUTCMilliseconds, "m": formatUTCMonthNumber, "M": formatUTCMinutes, "p": formatUTCPeriod, "q": formatUTCQuarter, "Q": formatUnixTimestamp, "s": formatUnixTimestampSeconds, "S": formatUTCSeconds, "u": formatUTCWeekdayNumberMonday, "U": formatUTCWeekNumberSunday, "V": formatUTCWeekNumberISO, "w": formatUTCWeekdayNumberSunday, "W": formatUTCWeekNumberMonday, "x": null, "X": null, "y": formatUTCYear, "Y": formatUTCFullYear, "Z": formatUTCZone, "%": formatLiteralPercent }; var parses = { "a": parseShortWeekday, "A": parseWeekday, "b": parseShortMonth, "B": parseMonth, "c": parseLocaleDateTime, "d": parseDayOfMonth, "e": parseDayOfMonth, "f": parseMicroseconds, "H": parseHour24, "I": parseHour24, "j": parseDayOfYear, "L": parseMilliseconds, "m": parseMonthNumber, "M": parseMinutes, "p": parsePeriod, "q": parseQuarter, "Q": parseUnixTimestamp, "s": parseUnixTimestampSeconds, "S": parseSeconds, "u": parseWeekdayNumberMonday, "U": parseWeekNumberSunday, "V": parseWeekNumberISO, "w": parseWeekdayNumberSunday, "W": parseWeekNumberMonday, "x": parseLocaleDate, "X": parseLocaleTime, "y": parseYear, "Y": parseFullYear, "Z": parseZone, "%": parseLiteralPercent }; // These recursive directive definitions must be deferred. formats.x = newFormat(locale_date, formats); formats.X = newFormat(locale_time, formats); formats.c = newFormat(locale_dateTime, formats); utcFormats.x = newFormat(locale_date, utcFormats); utcFormats.X = newFormat(locale_time, utcFormats); utcFormats.c = newFormat(locale_dateTime, utcFormats); function newFormat(specifier, formats) { return function(date) { var string = [], i = -1, j = 0, n = specifier.length, c, pad, format; if (!(date instanceof Date)) date = new Date(+date); while (++i < n) { if (specifier.charCodeAt(i) === 37) { string.push(specifier.slice(j, i)); if ((pad = pads[c = specifier.charAt(++i)]) != null) c = specifier.charAt(++i); else pad = c === "e" ? " " : "0"; if (format = formats[c]) c = format(date, pad); string.push(c); j = i + 1; } } string.push(specifier.slice(j, i)); return string.join(""); }; } function newParse(specifier, Z) { return function(string) { var d = newDate(1900, undefined, 1), i = parseSpecifier(d, specifier, string += "", 0), week, day$1; if (i != string.length) return null; // If a UNIX timestamp is specified, return it. if ("Q" in d) return new Date(d.Q); if ("s" in d) return new Date(d.s * 1000 + ("L" in d ? d.L : 0)); // If this is utcParse, never use the local timezone. if (Z && !("Z" in d)) d.Z = 0; // The am-pm flag is 0 for AM, and 1 for PM. if ("p" in d) d.H = d.H % 12 + d.p * 12; // If the month was not specified, inherit from the quarter. if (d.m === undefined) d.m = "q" in d ? d.q : 0; // Convert day-of-week and week-of-year to day-of-year. if ("V" in d) { if (d.V < 1 || d.V > 53) return null; if (!("w" in d)) d.w = 1; if ("Z" in d) { week = utcDate(newDate(d.y, 0, 1)), day$1 = week.getUTCDay(); week = day$1 > 4 || day$1 === 0 ? utcMonday.ceil(week) : utcMonday(week); week = utcDay.offset(week, (d.V - 1) * 7); d.y = week.getUTCFullYear(); d.m = week.getUTCMonth(); d.d = week.getUTCDate() + (d.w + 6) % 7; } else { week = localDate(newDate(d.y, 0, 1)), day$1 = week.getDay(); week = day$1 > 4 || day$1 === 0 ? monday.ceil(week) : monday(week); week = day.offset(week, (d.V - 1) * 7); d.y = week.getFullYear(); d.m = week.getMonth(); d.d = week.getDate() + (d.w + 6) % 7; } } else if ("W" in d || "U" in d) { if (!("w" in d)) d.w = "u" in d ? d.u % 7 : "W" in d ? 1 : 0; day$1 = "Z" in d ? utcDate(newDate(d.y, 0, 1)).getUTCDay() : localDate(newDate(d.y, 0, 1)).getDay(); d.m = 0; d.d = "W" in d ? (d.w + 6) % 7 + d.W * 7 - (day$1 + 5) % 7 : d.w + d.U * 7 - (day$1 + 6) % 7; } // If a time zone is specified, all fields are interpreted as UTC and then // offset according to the specified time zone. if ("Z" in d) { d.H += d.Z / 100 | 0; d.M += d.Z % 100; return utcDate(d); } // Otherwise, all fields are in local time. return localDate(d); }; } function parseSpecifier(d, specifier, string, j) { var i = 0, n = specifier.length, m = string.length, c, parse; while (i < n) { if (j >= m) return -1; c = specifier.charCodeAt(i++); if (c === 37) { c = specifier.charAt(i++); parse = parses[c in pads ? specifier.charAt(i++) : c]; if (!parse || ((j = parse(d, string, j)) < 0)) return -1; } else if (c != string.charCodeAt(j++)) { return -1; } } return j; } function parsePeriod(d, string, i) { var n = periodRe.exec(string.slice(i)); return n ? (d.p = periodLookup[n[0].toLowerCase()], i + n[0].length) : -1; } function parseShortWeekday(d, string, i) { var n = shortWeekdayRe.exec(string.slice(i)); return n ? (d.w = shortWeekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1; } function parseWeekday(d, string, i) { var n = weekdayRe.exec(string.slice(i)); return n ? (d.w = weekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1; } function parseShortMonth(d, string, i) { var n = shortMonthRe.exec(string.slice(i)); return n ? (d.m = shortMonthLookup[n[0].toLowerCase()], i + n[0].length) : -1; } function parseMonth(d, string, i) { var n = monthRe.exec(string.slice(i)); return n ? (d.m = monthLookup[n[0].toLowerCase()], i + n[0].length) : -1; } function parseLocaleDateTime(d, string, i) { return parseSpecifier(d, locale_dateTime, string, i); } function parseLocaleDate(d, string, i) { return parseSpecifier(d, locale_date, string, i); } function parseLocaleTime(d, string, i) { return parseSpecifier(d, locale_time, string, i); } function formatShortWeekday(d) { return locale_shortWeekdays[d.getDay()]; } function formatWeekday(d) { return locale_weekdays[d.getDay()]; } function formatShortMonth(d) { return locale_shortMonths[d.getMonth()]; } function formatMonth(d) { return locale_months[d.getMonth()]; } function formatPeriod(d) { return locale_periods[+(d.getHours() >= 12)]; } function formatQuarter(d) { return 1 + ~~(d.getMonth() / 3); } function formatUTCShortWeekday(d) { return locale_shortWeekdays[d.getUTCDay()]; } function formatUTCWeekday(d) { return locale_weekdays[d.getUTCDay()]; } function formatUTCShortMonth(d) { return locale_shortMonths[d.getUTCMonth()]; } function formatUTCMonth(d) { return locale_months[d.getUTCMonth()]; } function formatUTCPeriod(d) { return locale_periods[+(d.getUTCHours() >= 12)]; } function formatUTCQuarter(d) { return 1 + ~~(d.getUTCMonth() / 3); } return { format: function(specifier) { var f = newFormat(specifier += "", formats); f.toString = function() { return specifier; }; return f; }, parse: function(specifier) { var p = newParse(specifier += "", false); p.toString = function() { return specifier; }; return p; }, utcFormat: function(specifier) { var f = newFormat(specifier += "", utcFormats); f.toString = function() { return specifier; }; return f; }, utcParse: function(specifier) { var p = newParse(specifier += "", true); p.toString = function() { return specifier; }; return p; } }; } var pads = {"-": "", "_": " ", "0": "0"}, numberRe = /^\s*\d+/, // note: ignores next directive percentRe = /^%/, requoteRe = /[\\^$*+?|[\]().{}]/g; function pad(value, fill, width) { var sign = value < 0 ? "-" : "", string = (sign ? -value : value) + "", length = string.length; return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string); } function requote(s) { return s.replace(requoteRe, "\\$&"); } function formatRe(names) { return new RegExp("^(?:" + names.map(requote).join("|") + ")", "i"); } function formatLookup(names) { var map = {}, i = -1, n = names.length; while (++i < n) map[names[i].toLowerCase()] = i; return map; } function parseWeekdayNumberSunday(d, string, i) { var n = numberRe.exec(string.slice(i, i + 1)); return n ? (d.w = +n[0], i + n[0].length) : -1; } function parseWeekdayNumberMonday(d, string, i) { var n = numberRe.exec(string.slice(i, i + 1)); return n ? (d.u = +n[0], i + n[0].length) : -1; } function parseWeekNumberSunday(d, string, i) { var n = numberRe.exec(string.slice(i, i + 2)); return n ? (d.U = +n[0], i + n[0].length) : -1; } function parseWeekNumberISO(d, string, i) { var n = numberRe.exec(string.slice(i, i + 2)); return n ? (d.V = +n[0], i + n[0].length) : -1; } function parseWeekNumberMonday(d, string, i) { var n = numberRe.exec(string.slice(i, i + 2)); return n ? (d.W = +n[0], i + n[0].length) : -1; } function parseFullYear(d, string, i) { var n = numberRe.exec(string.slice(i, i + 4)); return n ? (d.y = +n[0], i + n[0].length) : -1; } function parseYear(d, string, i) { var n = numberRe.exec(string.slice(i, i + 2)); return n ? (d.y = +n[0] + (+n[0] > 68 ? 1900 : 2000), i + n[0].length) : -1; } function parseZone(d, string, i) { var n = /^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(string.slice(i, i + 6)); return n ? (d.Z = n[1] ? 0 : -(n[2] + (n[3] || "00")), i + n[0].length) : -1; } function parseQuarter(d, string, i) { var n = numberRe.exec(string.slice(i, i + 1)); return n ? (d.q = n[0] * 3 - 3, i + n[0].length) : -1; } function parseMonthNumber(d, string, i) { var n = numberRe.exec(string.slice(i, i + 2)); return n ? (d.m = n[0] - 1, i + n[0].length) : -1; } function parseDayOfMonth(d, string, i) { var n = numberRe.exec(string.slice(i, i + 2)); return n ? (d.d = +n[0], i + n[0].length) : -1; } function parseDayOfYear(d, string, i) { var n = numberRe.exec(string.slice(i, i + 3)); return n ? (d.m = 0, d.d = +n[0], i + n[0].length) : -1; } function parseHour24(d, string, i) { var n = numberRe.exec(string.slice(i, i + 2)); return n ? (d.H = +n[0], i + n[0].length) : -1; } function parseMinutes(d, string, i) { var n = numberRe.exec(string.slice(i, i + 2)); return n ? (d.M = +n[0], i + n[0].length) : -1; } function parseSeconds(d, string, i) { var n = numberRe.exec(string.slice(i, i + 2)); return n ? (d.S = +n[0], i + n[0].length) : -1; } function parseMilliseconds(d, string, i) { var n = numberRe.exec(string.slice(i, i + 3)); return n ? (d.L = +n[0], i + n[0].length) : -1; } function parseMicroseconds(d, string, i) { var n = numberRe.exec(string.slice(i, i + 6)); return n ? (d.L = Math.floor(n[0] / 1000), i + n[0].length) : -1; } function parseLiteralPercent(d, string, i) { var n = percentRe.exec(string.slice(i, i + 1)); return n ? i + n[0].length : -1; } function parseUnixTimestamp(d, string, i) { var n = numberRe.exec(string.slice(i)); return n ? (d.Q = +n[0], i + n[0].length) : -1; } function parseUnixTimestampSeconds(d, string, i) { var n = numberRe.exec(string.slice(i)); return n ? (d.s = +n[0], i + n[0].length) : -1; } function formatDayOfMonth(d, p) { return pad(d.getDate(), p, 2); } function formatHour24(d, p) { return pad(d.getHours(), p, 2); } function formatHour12(d, p) { return pad(d.getHours() % 12 || 12, p, 2); } function formatDayOfYear(d, p) { return pad(1 + day.count(year(d), d), p, 3); } function formatMilliseconds(d, p) { return pad(d.getMilliseconds(), p, 3); } function formatMicroseconds(d, p) { return formatMilliseconds(d, p) + "000"; } function formatMonthNumber(d, p) { return pad(d.getMonth() + 1, p, 2); } function formatMinutes(d, p) { return pad(d.getMinutes(), p, 2); } function formatSeconds(d, p) { return pad(d.getSeconds(), p, 2); } function formatWeekdayNumberMonday(d) { var day = d.getDay(); return day === 0 ? 7 : day; } function formatWeekNumberSunday(d, p) { return pad(sunday.count(year(d) - 1, d), p, 2); } function formatWeekNumberISO(d, p) { var day = d.getDay(); d = (day >= 4 || day === 0) ? thursday(d) : thursday.ceil(d); return pad(thursday.count(year(d), d) + (year(d).getDay() === 4), p, 2); } function formatWeekdayNumberSunday(d) { return d.getDay(); } function formatWeekNumberMonday(d, p) { return pad(monday.count(year(d) - 1, d), p, 2); } function formatYear(d, p) { return pad(d.getFullYear() % 100, p, 2); } function formatFullYear(d, p) { return pad(d.getFullYear() % 10000, p, 4); } function formatZone(d) { var z = d.getTimezoneOffset(); return (z > 0 ? "-" : (z *= -1, "+")) + pad(z / 60 | 0, "0", 2) + pad(z % 60, "0", 2); } function formatUTCDayOfMonth(d, p) { return pad(d.getUTCDate(), p, 2); } function formatUTCHour24(d, p) { return pad(d.getUTCHours(), p, 2); } function formatUTCHour12(d, p) { return pad(d.getUTCHours() % 12 || 12, p, 2); } function formatUTCDayOfYear(d, p) { return pad(1 + utcDay.count(utcYear(d), d), p, 3); } function formatUTCMilliseconds(d, p) { return pad(d.getUTCMilliseconds(), p, 3); } function formatUTCMicroseconds(d, p) { return formatUTCMilliseconds(d, p) + "000"; } function formatUTCMonthNumber(d, p) { return pad(d.getUTCMonth() + 1, p, 2); } function formatUTCMinutes(d, p) { return pad(d.getUTCMinutes(), p, 2); } function formatUTCSeconds(d, p) { return pad(d.getUTCSeconds(), p, 2); } function formatUTCWeekdayNumberMonday(d) { var dow = d.getUTCDay(); return dow === 0 ? 7 : dow; } function formatUTCWeekNumberSunday(d, p) { return pad(utcSunday.count(utcYear(d) - 1, d), p, 2); } function formatUTCWeekNumberISO(d, p) { var day = d.getUTCDay(); d = (day >= 4 || day === 0) ? utcThursday(d) : utcThursday.ceil(d); return pad(utcThursday.count(utcYear(d), d) + (utcYear(d).getUTCDay() === 4), p, 2); } function formatUTCWeekdayNumberSunday(d) { return d.getUTCDay(); } function formatUTCWeekNumberMonday(d, p) { return pad(utcMonday.count(utcYear(d) - 1, d), p, 2); } function formatUTCYear(d, p) { return pad(d.getUTCFullYear() % 100, p, 2); } function formatUTCFullYear(d, p) { return pad(d.getUTCFullYear() % 10000, p, 4); } function formatUTCZone() { return "+0000"; } function formatLiteralPercent() { return "%"; } function formatUnixTimestamp(d) { return +d; } function formatUnixTimestampSeconds(d) { return Math.floor(+d / 1000); } var locale$1; var timeFormat; var timeParse; var utcFormat; var utcParse; defaultLocale$1({ dateTime: "%x, %X", date: "%-m/%-d/%Y", time: "%-I:%M:%S %p", periods: ["AM", "PM"], days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], shortDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], shortMonths: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] }); function defaultLocale$1(definition) { locale$1 = formatLocale$1(definition); timeFormat = locale$1.format; timeParse = locale$1.parse; utcFormat = locale$1.utcFormat; utcParse = locale$1.utcParse; return locale$1; } /** * Helper functions to format numbers and dates. */ let formatter; let incognito; favaAPIStore.subscribe(favaAPI => { const { locale } = favaAPI.favaOptions; formatter = locale ? new Intl.NumberFormat(locale.replace("_", "-")).format : (formatter = format(".2f")); incognito = favaAPI.incognito ? num => num.replace(/[0-9]/g, "X") : num => num; }); function formatCurrency(number) { return incognito(formatter(number)); } const formatterPer = format(".2f"); function formatPercentage(number) { return `${formatterPer(Math.abs(number) * 100)}%`; } const formatterShort = format(".2s"); function formatCurrencyShort(number) { return incognito(formatterShort(number)); } /** Date formatters for human consumption. */ const dateFormat = { year: utcFormat("%Y"), quarter(date) { return `${date.getUTCFullYear()}Q${Math.floor(date.getUTCMonth() / 3) + 1}`; }, month: utcFormat("%b %Y"), week: utcFormat("%YW%W"), day: utcFormat("%Y-%m-%d"), }; /** Date formatters for the entry filter form. */ const timeFilterDateFormat = { year: utcFormat("%Y"), quarter(date) { return `${date.getUTCFullYear()}-Q${Math.floor(date.getUTCMonth() / 3) + 1}`; }, month: utcFormat("%Y-%m"), week: utcFormat("%Y-W%W"), day: utcFormat("%Y-%m-%d"), }; /** Today as a ISO-8601 date string. */ function todayAsString() { return timeFormat("%Y-%m-%d")(new Date()); } const currentDateFormat = derived(interval, val => dateFormat[val]); const currentTimeFilterDateFormat = derived(interval, val => timeFilterDateFormat[val]); var slice = Array.prototype.slice; function identity$3(x) { return x; } var top = 1, right = 2, bottom = 3, left = 4, epsilon = 1e-6; function translateX(x) { return "translate(" + (x + 0.5) + ",0)"; } function translateY(y) { return "translate(0," + (y + 0.5) + ")"; } function number$1(scale) { return function(d) { return +scale(d); }; } function center(scale) { var offset = Math.max(0, scale.bandwidth() - 1) / 2; // Adjust for 0.5px offset. if (scale.round()) offset = Math.round(offset); return function(d) { return +scale(d) + offset; }; } function entering() { return !this.__axis; } function axis(orient, scale) { var tickArguments = [], tickValues = null, tickFormat = null, tickSizeInner = 6, tickSizeOuter = 6, tickPadding = 3, k = orient === top || orient === left ? -1 : 1, x = orient === left || orient === right ? "x" : "y", transform = orient === top || orient === bottom ? translateX : translateY; function axis(context) { var values = tickValues == null ? (scale.ticks ? scale.ticks.apply(scale, tickArguments) : scale.domain()) : tickValues, format = tickFormat == null ? (scale.tickFormat ? scale.tickFormat.apply(scale, tickArguments) : identity$3) : tickFormat, spacing = Math.max(tickSizeInner, 0) + tickPadding, range = scale.range(), range0 = +range[0] + 0.5, range1 = +range[range.length - 1] + 0.5, position = (scale.bandwidth ? center : number$1)(scale.copy()), selection = context.selection ? context.selection() : context, path = selection.selectAll(".domain").data([null]), tick = selection.selectAll(".tick").data(values, scale).order(), tickExit = tick.exit(), tickEnter = tick.enter().append("g").attr("class", "tick"), line = tick.select("line"), text = tick.select("text"); path = path.merge(path.enter().insert("path", ".tick") .attr("class", "domain") .attr("stroke", "currentColor")); tick = tick.merge(tickEnter); line = line.merge(tickEnter.append("line") .attr("stroke", "currentColor") .attr(x + "2", k * tickSizeInner)); text = text.merge(tickEnter.append("text") .attr("fill", "currentColor") .attr(x, k * spacing) .attr("dy", orient === top ? "0em" : orient === bottom ? "0.71em" : "0.32em")); if (context !== selection) { path = path.transition(context); tick = tick.transition(context); line = line.transition(context); text = text.transition(context); tickExit = tickExit.transition(context) .attr("opacity", epsilon) .attr("transform", function(d) { return isFinite(d = position(d)) ? transform(d) : this.getAttribute("transform"); }); tickEnter .attr("opacity", epsilon) .attr("transform", function(d) { var p = this.parentNode.__axis; return transform(p && isFinite(p = p(d)) ? p : position(d)); }); } tickExit.remove(); path .attr("d", orient === left || orient == right ? (tickSizeOuter ? "M" + k * tickSizeOuter + "," + range0 + "H0.5V" + range1 + "H" + k * tickSizeOuter : "M0.5," + range0 + "V" + range1) : (tickSizeOuter ? "M" + range0 + "," + k * tickSizeOuter + "V0.5H" + range1 + "V" + k * tickSizeOuter : "M" + range0 + ",0.5H" + range1)); tick .attr("opacity", 1) .attr("transform", function(d) { return transform(position(d)); }); line .attr(x + "2", k * tickSizeInner); text .attr(x, k * spacing) .text(format); selection.filter(entering) .attr("fill", "none") .attr("font-size", 10) .attr("font-family", "sans-serif") .attr("text-anchor", orient === right ? "start" : orient === left ? "end" : "middle"); selection .each(function() { this.__axis = position; }); } axis.scale = function(_) { return arguments.length ? (scale = _, axis) : scale; }; axis.ticks = function() { return tickArguments = slice.call(arguments), axis; }; axis.tickArguments = function(_) { return arguments.length ? (tickArguments = _ == null ? [] : slice.call(_), axis) : tickArguments.slice(); }; axis.tickValues = function(_) { return arguments.length ? (tickValues = _ == null ? null : slice.call(_), axis) : tickValues && tickValues.slice(); }; axis.tickFormat = function(_) { return arguments.length ? (tickFormat = _, axis) : tickFormat; }; axis.tickSize = function(_) { return arguments.length ? (tickSizeInner = tickSizeOuter = +_, axis) : tickSizeInner; }; axis.tickSizeInner = function(_) { return arguments.length ? (tickSizeInner = +_, axis) : tickSizeInner; }; axis.tickSizeOuter = function(_) { return arguments.length ? (tickSizeOuter = +_, axis) : tickSizeOuter; }; axis.tickPadding = function(_) { return arguments.length ? (tickPadding = +_, axis) : tickPadding; }; return axis; } function axisBottom(scale) { return axis(bottom, scale); } function axisLeft(scale) { return axis(left, scale); } function initRange(domain, range) { switch (arguments.length) { case 0: break; case 1: this.range(domain); break; default: this.range(range).domain(domain); break; } return this; } const implicit = Symbol("implicit"); function ordinal() { var index = new Map(), domain = [], range = [], unknown = implicit; function scale(d) { var key = d + "", i = index.get(key); if (!i) { if (unknown !== implicit) return unknown; index.set(key, i = domain.push(d)); } return range[(i - 1) % range.length]; } scale.domain = function(_) { if (!arguments.length) return domain.slice(); domain = [], index = new Map(); for (const value of _) { const key = value + ""; if (index.has(key)) continue; index.set(key, domain.push(value)); } return scale; }; scale.range = function(_) { return arguments.length ? (range = Array.from(_), scale) : range.slice(); }; scale.unknown = function(_) { return arguments.length ? (unknown = _, scale) : unknown; }; scale.copy = function() { return ordinal(domain, range).unknown(unknown); }; initRange.apply(scale, arguments); return scale; } function band() { var scale = ordinal().unknown(undefined), domain = scale.domain, ordinalRange = scale.range, r0 = 0, r1 = 1, step, bandwidth, round = false, paddingInner = 0, paddingOuter = 0, align = 0.5; delete scale.unknown; function rescale() { var n = domain().length, reverse = r1 < r0, start = reverse ? r1 : r0, stop = reverse ? r0 : r1; step = (stop - start) / Math.max(1, n - paddingInner + paddingOuter * 2); if (round) step = Math.floor(step); start += (stop - start - step * (n - paddingInner)) * align; bandwidth = step * (1 - paddingInner); if (round) start = Math.round(start), bandwidth = Math.round(bandwidth); var values = sequence(n).map(function(i) { return start + step * i; }); return ordinalRange(reverse ? values.reverse() : values); } scale.domain = function(_) { return arguments.length ? (domain(_), rescale()) : domain(); }; scale.range = function(_) { return arguments.length ? ([r0, r1] = _, r0 = +r0, r1 = +r1, rescale()) : [r0, r1]; }; scale.rangeRound = function(_) { return [r0, r1] = _, r0 = +r0, r1 = +r1, round = true, rescale(); }; scale.bandwidth = function() { return bandwidth; }; scale.step = function() { return step; }; scale.round = function(_) { return arguments.length ? (round = !!_, rescale()) : round; }; scale.padding = function(_) { return arguments.length ? (paddingInner = Math.min(1, paddingOuter = +_), rescale()) : paddingInner; }; scale.paddingInner = function(_) { return arguments.length ? (paddingInner = Math.min(1, _), rescale()) : paddingInner; }; scale.paddingOuter = function(_) { return arguments.length ? (paddingOuter = +_, rescale()) : paddingOuter; }; scale.align = function(_) { return arguments.length ? (align = Math.max(0, Math.min(1, _)), rescale()) : align; }; scale.copy = function() { return band(domain(), [r0, r1]) .round(round) .paddingInner(paddingInner) .paddingOuter(paddingOuter) .align(align); }; return initRange.apply(rescale(), arguments); } function pointish(scale) { var copy = scale.copy; scale.padding = scale.paddingOuter; delete scale.paddingInner; delete scale.paddingOuter; scale.copy = function() { return pointish(copy()); }; return scale; } function point() { return pointish(band.apply(null, arguments).paddingInner(1)); } function constant$4(x) { return function() { return x; }; } function number$2(x) { return +x; } var unit = [0, 1]; function identity$4(x) { return x; } function normalize(a, b) { return (b -= (a = +a)) ? function(x) { return (x - a) / b; } : constant$4(isNaN(b) ? NaN : 0.5); } function clamper(a, b) { var t; if (a > b) t = a, a = b, b = t; return function(x) { return Math.max(a, Math.min(b, x)); }; } // normalize(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1]. // interpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding range value x in [a,b]. function bimap(domain, range, interpolate) { var d0 = domain[0], d1 = domain[1], r0 = range[0], r1 = range[1]; if (d1 < d0) d0 = normalize(d1, d0), r0 = interpolate(r1, r0); else d0 = normalize(d0, d1), r0 = interpolate(r0, r1); return function(x) { return r0(d0(x)); }; } function polymap(domain, range, interpolate) { var j = Math.min(domain.length, range.length) - 1, d = new Array(j), r = new Array(j), i = -1; // Reverse descending domains. if (domain[j] < domain[0]) { domain = domain.slice().reverse(); range = range.slice().reverse(); } while (++i < j) { d[i] = normalize(domain[i], domain[i + 1]); r[i] = interpolate(range[i], range[i + 1]); } return function(x) { var i = bisectRight(domain, x, 1, j) - 1; return r[i](d[i](x)); }; } function copy(source, target) { return target .domain(source.domain()) .range(source.range()) .interpolate(source.interpolate()) .clamp(source.clamp()) .unknown(source.unknown()); } function transformer() { var domain = unit, range = unit, interpolate$1 = interpolate, transform, untransform, unknown, clamp = identity$4, piecewise, output, input; function rescale() { var n = Math.min(domain.length, range.length); if (clamp !== identity$4) clamp = clamper(domain[0], domain[n - 1]); piecewise = n > 2 ? polymap : bimap; output = input = null; return scale; } function scale(x) { return isNaN(x = +x) ? unknown : (output || (output = piecewise(domain.map(transform), range, interpolate$1)))(transform(clamp(x))); } scale.invert = function(y) { return clamp(untransform((input || (input = piecewise(range, domain.map(transform), interpolateNumber)))(y))); }; scale.domain = function(_) { return arguments.length ? (domain = Array.from(_, number$2), rescale()) : domain.slice(); }; scale.range = function(_) { return arguments.length ? (range = Array.from(_), rescale()) : range.slice(); }; scale.rangeRound = function(_) { return range = Array.from(_), interpolate$1 = interpolateRound, rescale(); }; scale.clamp = function(_) { return arguments.length ? (clamp = _ ? true : identity$4, rescale()) : clamp !== identity$4; }; scale.interpolate = function(_) { return arguments.length ? (interpolate$1 = _, rescale()) : interpolate$1; }; scale.unknown = function(_) { return arguments.length ? (unknown = _, scale) : unknown; }; return function(t, u) { transform = t, untransform = u; return rescale(); }; } function continuous() { return transformer()(identity$4, identity$4); } function tickFormat(start, stop, count, specifier) { var step = tickStep(start, stop, count), precision; specifier = formatSpecifier(specifier == null ? ",f" : specifier); switch (specifier.type) { case "s": { var value = Math.max(Math.abs(start), Math.abs(stop)); if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision; return formatPrefix(specifier, value); } case "": case "e": case "g": case "p": case "r": { if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e"); break; } case "f": case "%": { if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2; break; } } return format(specifier); } function linearish(scale) { var domain = scale.domain; scale.ticks = function(count) { var d = domain(); return ticks(d[0], d[d.length - 1], count == null ? 10 : count); }; scale.tickFormat = function(count, specifier) { var d = domain(); return tickFormat(d[0], d[d.length - 1], count == null ? 10 : count, specifier); }; scale.nice = function(count) { if (count == null) count = 10; var d = domain(), i0 = 0, i1 = d.length - 1, start = d[i0], stop = d[i1], step; if (stop < start) { step = start, start = stop, stop = step; step = i0, i0 = i1, i1 = step; } step = tickIncrement(start, stop, count); if (step > 0) { start = Math.floor(start / step) * step; stop = Math.ceil(stop / step) * step; step = tickIncrement(start, stop, count); } else if (step < 0) { start = Math.ceil(start * step) / step; stop = Math.floor(stop * step) / step; step = tickIncrement(start, stop, count); } if (step > 0) { d[i0] = Math.floor(start / step) * step; d[i1] = Math.ceil(stop / step) * step; domain(d); } else if (step < 0) { d[i0] = Math.ceil(start * step) / step; d[i1] = Math.floor(stop * step) / step; domain(d); } return scale; }; return scale; } function linear$1() { var scale = continuous(); scale.copy = function() { return copy(scale, linear$1()); }; initRange.apply(scale, arguments); return linearish(scale); } function nice(domain, interval) { domain = domain.slice(); var i0 = 0, i1 = domain.length - 1, x0 = domain[i0], x1 = domain[i1], t; if (x1 < x0) { t = i0, i0 = i1, i1 = t; t = x0, x0 = x1, x1 = t; } domain[i0] = interval.floor(x0); domain[i1] = interval.ceil(x1); return domain; } function transformPow(exponent) { return function(x) { return x < 0 ? -Math.pow(-x, exponent) : Math.pow(x, exponent); }; } function transformSqrt(x) { return x < 0 ? -Math.sqrt(-x) : Math.sqrt(x); } function transformSquare(x) { return x < 0 ? -x * x : x * x; } function powish(transform) { var scale = transform(identity$4, identity$4), exponent = 1; function rescale() { return exponent === 1 ? transform(identity$4, identity$4) : exponent === 0.5 ? transform(transformSqrt, transformSquare) : transform(transformPow(exponent), transformPow(1 / exponent)); } scale.exponent = function(_) { return arguments.length ? (exponent = +_, rescale()) : exponent; }; return linearish(scale); } function pow() { var scale = powish(transformer()); scale.copy = function() { return copy(scale, pow()).exponent(scale.exponent()); }; initRange.apply(scale, arguments); return scale; } function sqrt() { return pow.apply(null, arguments).exponent(0.5); } var durationSecond$1 = 1000, durationMinute$1 = durationSecond$1 * 60, durationHour$1 = durationMinute$1 * 60, durationDay$1 = durationHour$1 * 24, durationWeek$1 = durationDay$1 * 7, durationMonth = durationDay$1 * 30, durationYear = durationDay$1 * 365; function date$2(t) { return new Date(t); } function number$3(t) { return t instanceof Date ? +t : +new Date(+t); } function calendar(year, month, week, day, hour, minute, second, millisecond, format) { var scale = continuous(), invert = scale.invert, domain = scale.domain; var formatMillisecond = format(".%L"), formatSecond = format(":%S"), formatMinute = format("%I:%M"), formatHour = format("%I %p"), formatDay = format("%a %d"), formatWeek = format("%b %d"), formatMonth = format("%B"), formatYear = format("%Y"); var tickIntervals = [ [second, 1, durationSecond$1], [second, 5, 5 * durationSecond$1], [second, 15, 15 * durationSecond$1], [second, 30, 30 * durationSecond$1], [minute, 1, durationMinute$1], [minute, 5, 5 * durationMinute$1], [minute, 15, 15 * durationMinute$1], [minute, 30, 30 * durationMinute$1], [ hour, 1, durationHour$1 ], [ hour, 3, 3 * durationHour$1 ], [ hour, 6, 6 * durationHour$1 ], [ hour, 12, 12 * durationHour$1 ], [ day, 1, durationDay$1 ], [ day, 2, 2 * durationDay$1 ], [ week, 1, durationWeek$1 ], [ month, 1, durationMonth ], [ month, 3, 3 * durationMonth ], [ year, 1, durationYear ] ]; function tickFormat(date) { return (second(date) < date ? formatMillisecond : minute(date) < date ? formatSecond : hour(date) < date ? formatMinute : day(date) < date ? formatHour : month(date) < date ? (week(date) < date ? formatDay : formatWeek) : year(date) < date ? formatMonth : formatYear)(date); } function tickInterval(interval, start, stop) { if (interval == null) interval = 10; // If a desired tick count is specified, pick a reasonable tick interval // based on the extent of the domain and a rough estimate of tick size. // Otherwise, assume interval is already a time interval and use it. if (typeof interval === "number") { var target = Math.abs(stop - start) / interval, i = bisector(function(i) { return i[2]; }).right(tickIntervals, target), step; if (i === tickIntervals.length) { step = tickStep(start / durationYear, stop / durationYear, interval); interval = year; } else if (i) { i = tickIntervals[target / tickIntervals[i - 1][2] < tickIntervals[i][2] / target ? i - 1 : i]; step = i[1]; interval = i[0]; } else { step = Math.max(tickStep(start, stop, interval), 1); interval = millisecond; } return interval.every(step); } return interval; } scale.invert = function(y) { return new Date(invert(y)); }; scale.domain = function(_) { return arguments.length ? domain(Array.from(_, number$3)) : domain().map(date$2); }; scale.ticks = function(interval) { var d = domain(), t0 = d[0], t1 = d[d.length - 1], r = t1 < t0, t; if (r) t = t0, t0 = t1, t1 = t; t = tickInterval(interval, t0, t1); t = t ? t.range(t0, t1 + 1) : []; // inclusive stop return r ? t.reverse() : t; }; scale.tickFormat = function(count, specifier) { return specifier == null ? tickFormat : format(specifier); }; scale.nice = function(interval) { var d = domain(); return (interval = tickInterval(interval, d[0], d[d.length - 1])) ? domain(nice(d, interval)) : scale; }; scale.copy = function() { return copy(scale, calendar(year, month, week, day, hour, minute, second, millisecond, format)); }; return scale; } function scaleUtc() { return initRange.apply(calendar(utcYear, utcMonth, utcSunday, utcDay, utcHour, utcMinute, second, millisecond, utcFormat).domain([Date.UTC(2000, 0, 1), Date.UTC(2000, 0, 2)]), arguments); } /* The base class for all charts. * * Provides the following methods: * * - setHeight(num): set the height of the chart, accounting for margins. * - setWidth(num): set the width of the chart, accounting for margins. * - set(property, value): set the given property of the chart class to value. * * Charts should implement the following methods: * * - constructor(svg): Initialise the chart, prepare for drawing it to the * given (which is a d3-selection). * - draw(data): Draw the chart for the given data. * - update(): Update the chart (after resize, toggling, etc) */ class BaseChart { constructor(svg) { svg.setAttribute("class", ""); svg.innerHTML = ""; this.svg = svg; this.margin = { top: 10, right: 10, bottom: 30, left: 40, }; this.outerHeight = 300; this.height = this.outerHeight - this.margin.top - this.margin.bottom; this.outerWidth = 500; this.width = this.outerWidth - this.margin.left - this.margin.right; } setHeight(d) { this.svg.setAttribute("height", `${d}`); this.outerHeight = d; this.height = d - this.margin.top - this.margin.bottom; return this; } setWidth(d) { this.svg.setAttribute("width", `${d}`); this.outerWidth = d; this.width = d - this.margin.left - this.margin.right; return this; } set(property, value) { this[property] = value; return this; } } const NO_MARGINS = { top: 0, right: 0, bottom: 0, left: 0, }; function setTimeFilter(date) { filters.update(fs => (Object.assign(Object.assign({}, fs), { time: get_store_value(currentTimeFilterDateFormat)(date) }))); } /* * Generate an array of colors. * * Uses the HCL color space in an attempt to generate colours that are * to be perceived to be of the same brightness. */ function hclColorRange(count, chroma = 45, lightness = 70) { const offset = 270; const delta = 360 / count; const colors = [...Array(count).keys()].map(index => { const hue = (index * delta + offset) % 360; return hcl(hue, chroma, lightness); }); return colors; } const colors10 = hclColorRange(10).map(c => c.toString()); const colors15 = hclColorRange(15, 30, 80).map(c => c.toString()); /* * The color scales for the charts. * * The scales for treemap and sunburst charts will be initialised with all * accounts on page init and currencies with all commodities. */ const scales = { treemap: ordinal(colors15), sunburst: ordinal(colors10), currencies: ordinal(colors10), scatterplot: ordinal(colors10), }; const tooltip = select$1(document.body) .append("div") .attr("class", "tooltip"); // Add a tooltip to the given selection. function addTooltip(selection, tooltipText) { selection .on("mouseenter", (d) => { tooltip.style("opacity", 1).html(tooltipText(d)); }) .on("mousemove", () => { tooltip .style("left", `${event.pageX}px`) .style("top", `${event.pageY - 15}px`); }) .on("mouseleave", () => { tooltip.style("opacity", 0); }); } e.on("page-loaded", () => { tooltip.style("opacity", 0); }); const maxColumnWidth = 100; class BarChart extends BaseChart { constructor(svg) { super(svg); this.x0 = band().padding(0.1); this.x1 = band(); this.y = linear$1(); this.xAxis = axisBottom(this.x0).tickSizeOuter(0); this.yAxis = axisLeft(this.y).tickFormat(formatCurrencyShort); this.svg.setAttribute("class", "barchart"); this.canvas = select$1(this.svg) .classed("barchart", true) .append("g"); this.xAxisSelection = this.canvas.append("g").attr("class", "x axis"); this.yAxisSelection = this.canvas.append("g").attr("class", "y axis"); this.groups = this.canvas.selectAll(".group"); this.groupboxes = this.groups.append("rect"); this.axisgroupboxes = this.groups.append("rect"); this.bars = this.groups.selectAll(".bar"); this.budgets = this.groups.selectAll(".budget"); } draw(data) { this.x0.domain(data.map(d => d.label)); this.x1.domain(data[0].values.map(d => d.name)); this.y.domain([ Math.min(0, min(data, d => min(d.values, x => x.value)) || 0), Math.max(0, max(data, d => max(d.values, x => x.value)) || 0), ]); this.groups = this.canvas .selectAll(".group") .data(data) .enter() .append("g") .attr("class", "group") .call(addTooltip, this.tooltipText); this.groupboxes = this.groups.append("rect").attr("class", "group-box"); this.axisgroupboxes = this.groups .append("rect") .on("click", d => { setTimeFilter(d.date); }) .attr("class", "axis-group-box"); this.bars = this.groups .selectAll(".bar") .data(d => d.values) .enter() .append("rect") .attr("class", "bar") .style("fill", d => scales.currencies(d.name)); this.budgets = this.groups .selectAll(".budget") .data(d => d.values) .enter() .append("rect") .attr("class", "budget"); this.update(); return this; } update() { const screenWidth = this.width; const maxWidth = this.groups.size() * maxColumnWidth; const offset = this.margin.left + Math.max(0, screenWidth - maxWidth) / 2; this.width = Math.min(screenWidth, maxWidth); this.setHeight(250); this.y.range([this.height, 0]); this.x0.range([0, this.width]); this.x1.range([0, this.x0.bandwidth()]); this.canvas.attr("transform", `translate(${offset},${this.margin.top})`); this.yAxis.tickSize(-this.width); this.xAxisSelection.attr("transform", `translate(0,${this.height})`); this.xAxis.tickValues(this.filterTicks(this.x0.domain())); this.xAxisSelection.call(this.xAxis); this.yAxisSelection.call(this.yAxis); this.groups.attr("transform", d => `translate(${this.x0(d.label)},0)`); this.groupboxes .attr("width", this.x0.bandwidth()) .attr("height", this.height); this.axisgroupboxes .attr("width", this.x0.bandwidth()) .attr("height", this.margin.bottom) .attr("transform", `translate(0,${this.height})`); this.budgets .attr("width", this.x1.bandwidth()) .attr("x", d => this.x1(d.name) || 0) .attr("y", d => this.y(Math.max(0, d.budget))) .attr("height", d => Math.abs(this.y(d.budget) - this.y(0))); this.bars .attr("width", this.x1.bandwidth()) .attr("x", d => this.x1(d.name) || 0) .attr("y", d => this.y(Math.max(0, d.value))) .attr("height", d => Math.abs(this.y(d.value) - this.y(0))); this.legend = { domain: this.x1.domain(), scale: scales.currencies, }; } filterTicks(domain) { const labelsCount = this.width / 70; if (domain.length <= labelsCount) { return domain; } const showIndices = Math.ceil(domain.length / labelsCount); return domain.filter((d, i) => i % showIndices === 0); } } var pi = Math.PI, tau = 2 * pi, epsilon$1 = 1e-6, tauEpsilon = tau - epsilon$1; function Path() { this._x0 = this._y0 = // start of current subpath this._x1 = this._y1 = null; // end of current subpath this._ = ""; } function path() { return new Path; } Path.prototype = path.prototype = { constructor: Path, moveTo: function(x, y) { this._ += "M" + (this._x0 = this._x1 = +x) + "," + (this._y0 = this._y1 = +y); }, closePath: function() { if (this._x1 !== null) { this._x1 = this._x0, this._y1 = this._y0; this._ += "Z"; } }, lineTo: function(x, y) { this._ += "L" + (this._x1 = +x) + "," + (this._y1 = +y); }, quadraticCurveTo: function(x1, y1, x, y) { this._ += "Q" + (+x1) + "," + (+y1) + "," + (this._x1 = +x) + "," + (this._y1 = +y); }, bezierCurveTo: function(x1, y1, x2, y2, x, y) { this._ += "C" + (+x1) + "," + (+y1) + "," + (+x2) + "," + (+y2) + "," + (this._x1 = +x) + "," + (this._y1 = +y); }, arcTo: function(x1, y1, x2, y2, r) { x1 = +x1, y1 = +y1, x2 = +x2, y2 = +y2, r = +r; var x0 = this._x1, y0 = this._y1, x21 = x2 - x1, y21 = y2 - y1, x01 = x0 - x1, y01 = y0 - y1, l01_2 = x01 * x01 + y01 * y01; // Is the radius negative? Error. if (r < 0) throw new Error("negative radius: " + r); // Is this path empty? Move to (x1,y1). if (this._x1 === null) { this._ += "M" + (this._x1 = x1) + "," + (this._y1 = y1); } // Or, is (x1,y1) coincident with (x0,y0)? Do nothing. else if (!(l01_2 > epsilon$1)); // Or, are (x0,y0), (x1,y1) and (x2,y2) collinear? // Equivalently, is (x1,y1) coincident with (x2,y2)? // Or, is the radius zero? Line to (x1,y1). else if (!(Math.abs(y01 * x21 - y21 * x01) > epsilon$1) || !r) { this._ += "L" + (this._x1 = x1) + "," + (this._y1 = y1); } // Otherwise, draw an arc! else { var x20 = x2 - x0, y20 = y2 - y0, l21_2 = x21 * x21 + y21 * y21, l20_2 = x20 * x20 + y20 * y20, l21 = Math.sqrt(l21_2), l01 = Math.sqrt(l01_2), l = r * Math.tan((pi - Math.acos((l21_2 + l01_2 - l20_2) / (2 * l21 * l01))) / 2), t01 = l / l01, t21 = l / l21; // If the start tangent is not coincident with (x0,y0), line to. if (Math.abs(t01 - 1) > epsilon$1) { this._ += "L" + (x1 + t01 * x01) + "," + (y1 + t01 * y01); } this._ += "A" + r + "," + r + ",0,0," + (+(y01 * x20 > x01 * y20)) + "," + (this._x1 = x1 + t21 * x21) + "," + (this._y1 = y1 + t21 * y21); } }, arc: function(x, y, r, a0, a1, ccw) { x = +x, y = +y, r = +r, ccw = !!ccw; var dx = r * Math.cos(a0), dy = r * Math.sin(a0), x0 = x + dx, y0 = y + dy, cw = 1 ^ ccw, da = ccw ? a0 - a1 : a1 - a0; // Is the radius negative? Error. if (r < 0) throw new Error("negative radius: " + r); // Is this path empty? Move to (x0,y0). if (this._x1 === null) { this._ += "M" + x0 + "," + y0; } // Or, is (x0,y0) not coincident with the previous point? Line to (x0,y0). else if (Math.abs(this._x1 - x0) > epsilon$1 || Math.abs(this._y1 - y0) > epsilon$1) { this._ += "L" + x0 + "," + y0; } // Is this arc empty? We’re done. if (!r) return; // Does the angle go the wrong way? Flip the direction. if (da < 0) da = da % tau + tau; // Is this a complete circle? Draw two arcs to complete the circle. if (da > tauEpsilon) { this._ += "A" + r + "," + r + ",0,1," + cw + "," + (x - dx) + "," + (y - dy) + "A" + r + "," + r + ",0,1," + cw + "," + (this._x1 = x0) + "," + (this._y1 = y0); } // Is this arc non-empty? Draw an arc! else if (da > epsilon$1) { this._ += "A" + r + "," + r + ",0," + (+(da >= pi)) + "," + cw + "," + (this._x1 = x + r * Math.cos(a1)) + "," + (this._y1 = y + r * Math.sin(a1)); } }, rect: function(x, y, w, h) { this._ += "M" + (this._x0 = this._x1 = +x) + "," + (this._y0 = this._y1 = +y) + "h" + (+w) + "v" + (+h) + "h" + (-w) + "Z"; }, toString: function() { return this._; } }; function constant$5(x) { return function constant() { return x; }; } var abs = Math.abs; var atan2 = Math.atan2; var cos = Math.cos; var max$1 = Math.max; var min$1 = Math.min; var sin = Math.sin; var sqrt$1 = Math.sqrt; var epsilon$2 = 1e-12; var pi$1 = Math.PI; var halfPi = pi$1 / 2; var tau$1 = 2 * pi$1; function acos(x) { return x > 1 ? 0 : x < -1 ? pi$1 : Math.acos(x); } function asin(x) { return x >= 1 ? halfPi : x <= -1 ? -halfPi : Math.asin(x); } function arcInnerRadius(d) { return d.innerRadius; } function arcOuterRadius(d) { return d.outerRadius; } function arcStartAngle(d) { return d.startAngle; } function arcEndAngle(d) { return d.endAngle; } function arcPadAngle(d) { return d && d.padAngle; // Note: optional! } function intersect(x0, y0, x1, y1, x2, y2, x3, y3) { var x10 = x1 - x0, y10 = y1 - y0, x32 = x3 - x2, y32 = y3 - y2, t = y32 * x10 - x32 * y10; if (t * t < epsilon$2) return; t = (x32 * (y0 - y2) - y32 * (x0 - x2)) / t; return [x0 + t * x10, y0 + t * y10]; } // Compute perpendicular offset line of length rc. // http://mathworld.wolfram.com/Circle-LineIntersection.html function cornerTangents(x0, y0, x1, y1, r1, rc, cw) { var x01 = x0 - x1, y01 = y0 - y1, lo = (cw ? rc : -rc) / sqrt$1(x01 * x01 + y01 * y01), ox = lo * y01, oy = -lo * x01, x11 = x0 + ox, y11 = y0 + oy, x10 = x1 + ox, y10 = y1 + oy, x00 = (x11 + x10) / 2, y00 = (y11 + y10) / 2, dx = x10 - x11, dy = y10 - y11, d2 = dx * dx + dy * dy, r = r1 - rc, D = x11 * y10 - x10 * y11, d = (dy < 0 ? -1 : 1) * sqrt$1(max$1(0, r * r * d2 - D * D)), cx0 = (D * dy - dx * d) / d2, cy0 = (-D * dx - dy * d) / d2, cx1 = (D * dy + dx * d) / d2, cy1 = (-D * dx + dy * d) / d2, dx0 = cx0 - x00, dy0 = cy0 - y00, dx1 = cx1 - x00, dy1 = cy1 - y00; // Pick the closer of the two intersection points. // TODO Is there a faster way to determine which intersection to use? if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1; return { cx: cx0, cy: cy0, x01: -ox, y01: -oy, x11: cx0 * (r1 / r - 1), y11: cy0 * (r1 / r - 1) }; } function arc() { var innerRadius = arcInnerRadius, outerRadius = arcOuterRadius, cornerRadius = constant$5(0), padRadius = null, startAngle = arcStartAngle, endAngle = arcEndAngle, padAngle = arcPadAngle, context = null; function arc() { var buffer, r, r0 = +innerRadius.apply(this, arguments), r1 = +outerRadius.apply(this, arguments), a0 = startAngle.apply(this, arguments) - halfPi, a1 = endAngle.apply(this, arguments) - halfPi, da = abs(a1 - a0), cw = a1 > a0; if (!context) context = buffer = path(); // Ensure that the outer radius is always larger than the inner radius. if (r1 < r0) r = r1, r1 = r0, r0 = r; // Is it a point? if (!(r1 > epsilon$2)) context.moveTo(0, 0); // Or is it a circle or annulus? else if (da > tau$1 - epsilon$2) { context.moveTo(r1 * cos(a0), r1 * sin(a0)); context.arc(0, 0, r1, a0, a1, !cw); if (r0 > epsilon$2) { context.moveTo(r0 * cos(a1), r0 * sin(a1)); context.arc(0, 0, r0, a1, a0, cw); } } // Or is it a circular or annular sector? else { var a01 = a0, a11 = a1, a00 = a0, a10 = a1, da0 = da, da1 = da, ap = padAngle.apply(this, arguments) / 2, rp = (ap > epsilon$2) && (padRadius ? +padRadius.apply(this, arguments) : sqrt$1(r0 * r0 + r1 * r1)), rc = min$1(abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments)), rc0 = rc, rc1 = rc, t0, t1; // Apply padding? Note that since r1 ≥ r0, da1 ≥ da0. if (rp > epsilon$2) { var p0 = asin(rp / r0 * sin(ap)), p1 = asin(rp / r1 * sin(ap)); if ((da0 -= p0 * 2) > epsilon$2) p0 *= (cw ? 1 : -1), a00 += p0, a10 -= p0; else da0 = 0, a00 = a10 = (a0 + a1) / 2; if ((da1 -= p1 * 2) > epsilon$2) p1 *= (cw ? 1 : -1), a01 += p1, a11 -= p1; else da1 = 0, a01 = a11 = (a0 + a1) / 2; } var x01 = r1 * cos(a01), y01 = r1 * sin(a01), x10 = r0 * cos(a10), y10 = r0 * sin(a10); // Apply rounded corners? if (rc > epsilon$2) { var x11 = r1 * cos(a11), y11 = r1 * sin(a11), x00 = r0 * cos(a00), y00 = r0 * sin(a00), oc; // Restrict the corner radius according to the sector angle. if (da < pi$1 && (oc = intersect(x01, y01, x00, y00, x11, y11, x10, y10))) { var ax = x01 - oc[0], ay = y01 - oc[1], bx = x11 - oc[0], by = y11 - oc[1], kc = 1 / sin(acos((ax * bx + ay * by) / (sqrt$1(ax * ax + ay * ay) * sqrt$1(bx * bx + by * by))) / 2), lc = sqrt$1(oc[0] * oc[0] + oc[1] * oc[1]); rc0 = min$1(rc, (r0 - lc) / (kc - 1)); rc1 = min$1(rc, (r1 - lc) / (kc + 1)); } } // Is the sector collapsed to a line? if (!(da1 > epsilon$2)) context.moveTo(x01, y01); // Does the sector’s outer ring have rounded corners? else if (rc1 > epsilon$2) { t0 = cornerTangents(x00, y00, x01, y01, r1, rc1, cw); t1 = cornerTangents(x11, y11, x10, y10, r1, rc1, cw); context.moveTo(t0.cx + t0.x01, t0.cy + t0.y01); // Have the corners merged? if (rc1 < rc) context.arc(t0.cx, t0.cy, rc1, atan2(t0.y01, t0.x01), atan2(t1.y01, t1.x01), !cw); // Otherwise, draw the two corners and the ring. else { context.arc(t0.cx, t0.cy, rc1, atan2(t0.y01, t0.x01), atan2(t0.y11, t0.x11), !cw); context.arc(0, 0, r1, atan2(t0.cy + t0.y11, t0.cx + t0.x11), atan2(t1.cy + t1.y11, t1.cx + t1.x11), !cw); context.arc(t1.cx, t1.cy, rc1, atan2(t1.y11, t1.x11), atan2(t1.y01, t1.x01), !cw); } } // Or is the outer ring just a circular arc? else context.moveTo(x01, y01), context.arc(0, 0, r1, a01, a11, !cw); // Is there no inner ring, and it’s a circular sector? // Or perhaps it’s an annular sector collapsed due to padding? if (!(r0 > epsilon$2) || !(da0 > epsilon$2)) context.lineTo(x10, y10); // Does the sector’s inner ring (or point) have rounded corners? else if (rc0 > epsilon$2) { t0 = cornerTangents(x10, y10, x11, y11, r0, -rc0, cw); t1 = cornerTangents(x01, y01, x00, y00, r0, -rc0, cw); context.lineTo(t0.cx + t0.x01, t0.cy + t0.y01); // Have the corners merged? if (rc0 < rc) context.arc(t0.cx, t0.cy, rc0, atan2(t0.y01, t0.x01), atan2(t1.y01, t1.x01), !cw); // Otherwise, draw the two corners and the ring. else { context.arc(t0.cx, t0.cy, rc0, atan2(t0.y01, t0.x01), atan2(t0.y11, t0.x11), !cw); context.arc(0, 0, r0, atan2(t0.cy + t0.y11, t0.cx + t0.x11), atan2(t1.cy + t1.y11, t1.cx + t1.x11), cw); context.arc(t1.cx, t1.cy, rc0, atan2(t1.y11, t1.x11), atan2(t1.y01, t1.x01), !cw); } } // Or is the inner ring just a circular arc? else context.arc(0, 0, r0, a10, a00, cw); } context.closePath(); if (buffer) return context = null, buffer + "" || null; } arc.centroid = function() { var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2, a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - pi$1 / 2; return [cos(a) * r, sin(a) * r]; }; arc.innerRadius = function(_) { return arguments.length ? (innerRadius = typeof _ === "function" ? _ : constant$5(+_), arc) : innerRadius; }; arc.outerRadius = function(_) { return arguments.length ? (outerRadius = typeof _ === "function" ? _ : constant$5(+_), arc) : outerRadius; }; arc.cornerRadius = function(_) { return arguments.length ? (cornerRadius = typeof _ === "function" ? _ : constant$5(+_), arc) : cornerRadius; }; arc.padRadius = function(_) { return arguments.length ? (padRadius = _ == null ? null : typeof _ === "function" ? _ : constant$5(+_), arc) : padRadius; }; arc.startAngle = function(_) { return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$5(+_), arc) : startAngle; }; arc.endAngle = function(_) { return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$5(+_), arc) : endAngle; }; arc.padAngle = function(_) { return arguments.length ? (padAngle = typeof _ === "function" ? _ : constant$5(+_), arc) : padAngle; }; arc.context = function(_) { return arguments.length ? ((context = _ == null ? null : _), arc) : context; }; return arc; } function Linear(context) { this._context = context; } Linear.prototype = { areaStart: function() { this._line = 0; }, areaEnd: function() { this._line = NaN; }, lineStart: function() { this._point = 0; }, lineEnd: function() { if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); this._line = 1 - this._line; }, point: function(x, y) { x = +x, y = +y; switch (this._point) { case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; case 1: this._point = 2; // proceed default: this._context.lineTo(x, y); break; } } }; function curveLinear(context) { return new Linear(context); } function x(p) { return p[0]; } function y(p) { return p[1]; } function line() { var x$1 = x, y$1 = y, defined = constant$5(true), context = null, curve = curveLinear, output = null; function line(data) { var i, n = data.length, d, defined0 = false, buffer; if (context == null) output = curve(buffer = path()); for (i = 0; i <= n; ++i) { if (!(i < n && defined(d = data[i], i, data)) === defined0) { if (defined0 = !defined0) output.lineStart(); else output.lineEnd(); } if (defined0) output.point(+x$1(d, i, data), +y$1(d, i, data)); } if (buffer) return output = null, buffer + "" || null; } line.x = function(_) { return arguments.length ? (x$1 = typeof _ === "function" ? _ : constant$5(+_), line) : x$1; }; line.y = function(_) { return arguments.length ? (y$1 = typeof _ === "function" ? _ : constant$5(+_), line) : y$1; }; line.defined = function(_) { return arguments.length ? (defined = typeof _ === "function" ? _ : constant$5(!!_), line) : defined; }; line.curve = function(_) { return arguments.length ? (curve = _, context != null && (output = curve(context)), line) : curve; }; line.context = function(_) { return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), line) : context; }; return line; } function tree_add(d) { var x = +this._x.call(null, d), y = +this._y.call(null, d); return add(this.cover(x, y), x, y, d); } function add(tree, x, y, d) { if (isNaN(x) || isNaN(y)) return tree; // ignore invalid points var parent, node = tree._root, leaf = {data: d}, x0 = tree._x0, y0 = tree._y0, x1 = tree._x1, y1 = tree._y1, xm, ym, xp, yp, right, bottom, i, j; // If the tree is empty, initialize the root as a leaf. if (!node) return tree._root = leaf, tree; // Find the existing leaf for the new point, or add it. while (node.length) { if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym; if (parent = node, !(node = node[i = bottom << 1 | right])) return parent[i] = leaf, tree; } // Is the new point is exactly coincident with the existing point? xp = +tree._x.call(null, node.data); yp = +tree._y.call(null, node.data); if (x === xp && y === yp) return leaf.next = node, parent ? parent[i] = leaf : tree._root = leaf, tree; // Otherwise, split the leaf node until the old and new point are separated. do { parent = parent ? parent[i] = new Array(4) : tree._root = new Array(4); if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym; } while ((i = bottom << 1 | right) === (j = (yp >= ym) << 1 | (xp >= xm))); return parent[j] = node, parent[i] = leaf, tree; } function addAll(data) { var d, i, n = data.length, x, y, xz = new Array(n), yz = new Array(n), x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity; // Compute the points and their extent. for (i = 0; i < n; ++i) { if (isNaN(x = +this._x.call(null, d = data[i])) || isNaN(y = +this._y.call(null, d))) continue; xz[i] = x; yz[i] = y; if (x < x0) x0 = x; if (x > x1) x1 = x; if (y < y0) y0 = y; if (y > y1) y1 = y; } // If there were no (valid) points, abort. if (x0 > x1 || y0 > y1) return this; // Expand the tree to cover the new points. this.cover(x0, y0).cover(x1, y1); // Add the new points. for (i = 0; i < n; ++i) { add(this, xz[i], yz[i], data[i]); } return this; } function tree_cover(x, y) { if (isNaN(x = +x) || isNaN(y = +y)) return this; // ignore invalid points var x0 = this._x0, y0 = this._y0, x1 = this._x1, y1 = this._y1; // If the quadtree has no extent, initialize them. // Integer extent are necessary so that if we later double the extent, // the existing quadrant boundaries don’t change due to floating point error! if (isNaN(x0)) { x1 = (x0 = Math.floor(x)) + 1; y1 = (y0 = Math.floor(y)) + 1; } // Otherwise, double repeatedly to cover. else { var z = x1 - x0, node = this._root, parent, i; while (x0 > x || x >= x1 || y0 > y || y >= y1) { i = (y < y0) << 1 | (x < x0); parent = new Array(4), parent[i] = node, node = parent, z *= 2; switch (i) { case 0: x1 = x0 + z, y1 = y0 + z; break; case 1: x0 = x1 - z, y1 = y0 + z; break; case 2: x1 = x0 + z, y0 = y1 - z; break; case 3: x0 = x1 - z, y0 = y1 - z; break; } } if (this._root && this._root.length) this._root = node; } this._x0 = x0; this._y0 = y0; this._x1 = x1; this._y1 = y1; return this; } function tree_data() { var data = []; this.visit(function(node) { if (!node.length) do data.push(node.data); while (node = node.next) }); return data; } function tree_extent(_) { return arguments.length ? this.cover(+_[0][0], +_[0][1]).cover(+_[1][0], +_[1][1]) : isNaN(this._x0) ? undefined : [[this._x0, this._y0], [this._x1, this._y1]]; } function Quad(node, x0, y0, x1, y1) { this.node = node; this.x0 = x0; this.y0 = y0; this.x1 = x1; this.y1 = y1; } function tree_find(x, y, radius) { var data, x0 = this._x0, y0 = this._y0, x1, y1, x2, y2, x3 = this._x1, y3 = this._y1, quads = [], node = this._root, q, i; if (node) quads.push(new Quad(node, x0, y0, x3, y3)); if (radius == null) radius = Infinity; else { x0 = x - radius, y0 = y - radius; x3 = x + radius, y3 = y + radius; radius *= radius; } while (q = quads.pop()) { // Stop searching if this quadrant can’t contain a closer node. if (!(node = q.node) || (x1 = q.x0) > x3 || (y1 = q.y0) > y3 || (x2 = q.x1) < x0 || (y2 = q.y1) < y0) continue; // Bisect the current quadrant. if (node.length) { var xm = (x1 + x2) / 2, ym = (y1 + y2) / 2; quads.push( new Quad(node[3], xm, ym, x2, y2), new Quad(node[2], x1, ym, xm, y2), new Quad(node[1], xm, y1, x2, ym), new Quad(node[0], x1, y1, xm, ym) ); // Visit the closest quadrant first. if (i = (y >= ym) << 1 | (x >= xm)) { q = quads[quads.length - 1]; quads[quads.length - 1] = quads[quads.length - 1 - i]; quads[quads.length - 1 - i] = q; } } // Visit this point. (Visiting coincident points isn’t necessary!) else { var dx = x - +this._x.call(null, node.data), dy = y - +this._y.call(null, node.data), d2 = dx * dx + dy * dy; if (d2 < radius) { var d = Math.sqrt(radius = d2); x0 = x - d, y0 = y - d; x3 = x + d, y3 = y + d; data = node.data; } } } return data; } function tree_remove(d) { if (isNaN(x = +this._x.call(null, d)) || isNaN(y = +this._y.call(null, d))) return this; // ignore invalid points var parent, node = this._root, retainer, previous, next, x0 = this._x0, y0 = this._y0, x1 = this._x1, y1 = this._y1, x, y, xm, ym, right, bottom, i, j; // If the tree is empty, initialize the root as a leaf. if (!node) return this; // Find the leaf node for the point. // While descending, also retain the deepest parent with a non-removed sibling. if (node.length) while (true) { if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym; if (!(parent = node, node = node[i = bottom << 1 | right])) return this; if (!node.length) break; if (parent[(i + 1) & 3] || parent[(i + 2) & 3] || parent[(i + 3) & 3]) retainer = parent, j = i; } // Find the point to remove. while (node.data !== d) if (!(previous = node, node = node.next)) return this; if (next = node.next) delete node.next; // If there are multiple coincident points, remove just the point. if (previous) return (next ? previous.next = next : delete previous.next), this; // If this is the root point, remove it. if (!parent) return this._root = next, this; // Remove this leaf. next ? parent[i] = next : delete parent[i]; // If the parent now contains exactly one leaf, collapse superfluous parents. if ((node = parent[0] || parent[1] || parent[2] || parent[3]) && node === (parent[3] || parent[2] || parent[1] || parent[0]) && !node.length) { if (retainer) retainer[j] = node; else this._root = node; } return this; } function removeAll(data) { for (var i = 0, n = data.length; i < n; ++i) this.remove(data[i]); return this; } function tree_root() { return this._root; } function tree_size() { var size = 0; this.visit(function(node) { if (!node.length) do ++size; while (node = node.next) }); return size; } function tree_visit(callback) { var quads = [], q, node = this._root, child, x0, y0, x1, y1; if (node) quads.push(new Quad(node, this._x0, this._y0, this._x1, this._y1)); while (q = quads.pop()) { if (!callback(node = q.node, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1) && node.length) { var xm = (x0 + x1) / 2, ym = (y0 + y1) / 2; if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1)); if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1)); if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym)); if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym)); } } return this; } function tree_visitAfter(callback) { var quads = [], next = [], q; if (this._root) quads.push(new Quad(this._root, this._x0, this._y0, this._x1, this._y1)); while (q = quads.pop()) { var node = q.node; if (node.length) { var child, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1, xm = (x0 + x1) / 2, ym = (y0 + y1) / 2; if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym)); if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym)); if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1)); if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1)); } next.push(q); } while (q = next.pop()) { callback(q.node, q.x0, q.y0, q.x1, q.y1); } return this; } function defaultX(d) { return d[0]; } function tree_x(_) { return arguments.length ? (this._x = _, this) : this._x; } function defaultY(d) { return d[1]; } function tree_y(_) { return arguments.length ? (this._y = _, this) : this._y; } function quadtree(nodes, x, y) { var tree = new Quadtree(x == null ? defaultX : x, y == null ? defaultY : y, NaN, NaN, NaN, NaN); return nodes == null ? tree : tree.addAll(nodes); } function Quadtree(x, y, x0, y0, x1, y1) { this._x = x; this._y = y; this._x0 = x0; this._y0 = y0; this._x1 = x1; this._y1 = y1; this._root = undefined; } function leaf_copy(leaf) { var copy = {data: leaf.data}, next = copy; while (leaf = leaf.next) next = next.next = {data: leaf.data}; return copy; } var treeProto = quadtree.prototype = Quadtree.prototype; treeProto.copy = function() { var copy = new Quadtree(this._x, this._y, this._x0, this._y0, this._x1, this._y1), node = this._root, nodes, child; if (!node) return copy; if (!node.length) return copy._root = leaf_copy(node), copy; nodes = [{source: node, target: copy._root = new Array(4)}]; while (node = nodes.pop()) { for (var i = 0; i < 4; ++i) { if (child = node.source[i]) { if (child.length) nodes.push({source: child, target: node.target[i] = new Array(4)}); else node.target[i] = leaf_copy(child); } } } return copy; }; treeProto.add = tree_add; treeProto.addAll = addAll; treeProto.cover = tree_cover; treeProto.data = tree_data; treeProto.extent = tree_extent; treeProto.find = tree_find; treeProto.remove = tree_remove; treeProto.removeAll = removeAll; treeProto.root = tree_root; treeProto.size = tree_size; treeProto.visit = tree_visit; treeProto.visitAfter = tree_visitAfter; treeProto.x = tree_x; treeProto.y = tree_y; class LineChart extends BaseChart { constructor(svg) { super(svg); this.data = []; this.x = scaleUtc(); this.y = linear$1(); this.xAxis = axisBottom(this.x).tickSizeOuter(0); this.yAxis = axisLeft(this.y) .tickPadding(6) .tickFormat(formatCurrencyShort); this.line = line() .x(d => this.x(d.date)) .y(d => this.y(d.value)); this.canvas = select$1(this.svg) .classed("linechart", true) .append("g"); this.xAxisSelection = this.canvas.append("g").attr("class", "x axis"); this.yAxisSelection = this.canvas.append("g").attr("class", "y axis"); this.quadtree = quadtree(); this.lines = this.canvas.selectAll(".line"); this.dots = this.canvas.selectAll("g.dot").selectAll("circle"); } draw(data) { this.data = data; this.x.domain([ min(this.data, s => s.values[0].date) || 0, max(this.data, s => s.values[s.values.length - 1].date) || 0, ]); // Span y-axis as max minus min value plus 5 percent margin const minDataValue = min(this.data, d => min(d.values, x => x.value)); const maxDataValue = max(this.data, d => max(d.values, x => x.value)); if (minDataValue !== undefined && maxDataValue !== undefined) { this.y.domain([ minDataValue - (maxDataValue - minDataValue) * 0.05, maxDataValue + (maxDataValue - minDataValue) * 0.05, ]); } this.lines = this.canvas .selectAll(".line") .data(data) .enter() .append("path") .attr("class", "line") .style("stroke", d => scales.currencies(d.name)); this.dots = this.canvas .selectAll("g.dot") .data(data) .enter() .append("g") .attr("class", "dot") .style("fill", d => scales.currencies(d.name)) .selectAll("circle") .data(d => d.values) .enter() .append("circle") .attr("r", 3); const canvasNode = this.canvas.node(); this.canvas .on("mousemove", () => { const matrix = canvasNode.getScreenCTM(); const d = this.quadtree.find(...clientPoint(canvasNode, event)); if (this.tooltipText && matrix && d) { tooltip .style("opacity", 1) .html(this.tooltipText(d)) .style("left", `${window.scrollX + this.x(d.date) + matrix.e}px`) .style("top", `${window.scrollY + this.y(d.value) + matrix.f - 15}px`); } else { tooltip.style("opacity", 0); } }) .on("mouseleave", () => { tooltip.style("opacity", 0); }); this.update(); return this; } update() { this.setHeight(250); this.y.range([this.height, 0]); this.x.range([0, this.width]); this.canvas.attr("transform", `translate(${this.margin.left},${this.margin.top})`); this.yAxis.tickSize(-this.width); this.xAxisSelection.attr("transform", `translate(0,${this.height})`); this.xAxisSelection.call(this.xAxis); this.yAxisSelection.call(this.yAxis); this.dots.attr("cx", d => this.x(d.date)).attr("cy", d => this.y(d.value)); this.lines.attr("d", d => this.line(d.values)); this.quadtree = quadtree(merge(this.data.map(d => d.values)), d => this.x(d.date), d => this.y(d.value)); this.legend = { domain: this.data.map(d => d.name), scale: scales.currencies, }; } } class ScatterPlot extends BaseChart { constructor(svg) { super(svg); this.canvas = select$1(this.svg) .classed("scatterplot", true) .append("g"); this.margin.left = 70; this.x = scaleUtc(); this.y = point().padding(1); this.xAxis = axisBottom(this.x).tickSizeOuter(0); this.yAxis = axisLeft(this.y) .tickPadding(6) .tickFormat(d => d); this.data = []; this.quadtree = quadtree(); this.xAxisSelection = this.canvas.append("g").attr("class", "x axis"); this.yAxisSelection = this.canvas.append("g").attr("class", "y axis"); this.dots = this.canvas.selectAll(".dot"); } draw(data) { this.data = data; const dateExtent = extent(data, d => d.date); if (dateExtent[0] !== undefined) { this.x.domain(dateExtent); } this.y.domain(data.map(d => d.type)); this.dots = this.canvas .selectAll(".dot") .data(this.data) .enter() .append("circle") .attr("class", "dot") .attr("r", 5) .style("fill", d => scales.scatterplot(d.type)); const canvasNode = this.canvas.node(); this.canvas .on("mousemove", () => { const matrix = canvasNode.getScreenCTM(); if (!matrix) { return; } const d = this.quadtree.find(...clientPoint(canvasNode, event)); if (d) { tooltip .style("opacity", 1) .html(this.tooltipText(d)) .style("left", `${window.scrollX + this.x(d.date) + matrix.e}px`) .style("top", `${window.scrollY + this.y(d.type) + matrix.f - 15}px`); } else { tooltip.style("opacity", 0); } }) .on("mouseleave", () => { tooltip.style("opacity", 0); }); this.update(); return this; } // eslint-disable-next-line class-methods-use-this tooltipText(d) { return `${d.description}${dateFormat.day(d.date)}`; } update() { this.setHeight(250); this.y.range([this.height, 0]); this.x.range([0, this.width]); this.canvas.attr("transform", `translate(${this.margin.left},${this.margin.top})`); this.yAxis.tickSize(-this.width); this.xAxisSelection.attr("transform", `translate(0,${this.height})`); this.xAxisSelection.call(this.xAxis); this.yAxisSelection.call(this.yAxis); this.dots.attr("cx", d => this.x(d.date)).attr("cy", d => this.y(d.type)); this.quadtree = quadtree(this.data, d => this.x(d.date), d => this.y(d.type)); } } /** * Add internal nodes as fake leaf nodes to their own children. * * In the treemap, we only render leaf nodes, so for accounts that have both * children and a balance, we want to duplicate them as leaf nodes. */ function addInternalNodesAsLeaves(node) { if (node.children.length) { node.children.forEach(addInternalNodesAsLeaves); node.children.push(Object.assign(Object.assign({}, node), { children: [], dummy: true })); node.balance = {}; } } // Turn the elements in the selection (assuming they have a .account attribute) // into links to the account page. function makeAccountLink(selection) { selection.on("click", d => { window.location.href = favaAPI.accountURL.replace("REPLACEME", d.data.account); event.stopPropagation(); }); } class TreeMapChart extends BaseChart { constructor(svg) { super(svg); this.treemap = treemap().paddingInner(2); this.margin = NO_MARGINS; this.canvas = select$1(svg).classed("treemap", true); this.cells = this.canvas.selectAll("g"); this.labels = this.cells.append("text"); } draw(data) { this.root = this.treemap(data); this.cells = this.canvas .selectAll("g") .data(this.root.leaves()) .enter() .append("g") .call(addTooltip, this.tooltipText); this.cells.append("rect").attr("fill", d => { const node = d.data.dummy ? d.parent : d; if (node.parent === this.root || !node.parent) { return scales.treemap(node.data.account); } return scales.treemap(node.parent.data.account); }); this.labels = this.cells .append("text") .attr("dy", ".5em") .attr("text-anchor", "middle") .text(d => d.data.account.split(":").pop() || "") .style("opacity", 0) .call(makeAccountLink); this.update(); return this; } update() { this.setHeight(Math.min(this.width / 2.5, 400)); if (!this.root) { return; } this.treemap.size([this.width, this.height]); this.treemap(this.root); function labelOpacity(d) { const length = this.getComputedTextLength(); return d.x1 - d.x0 > length + 4 && d.y1 - d.y0 > 14 ? 1 : 0; } this.cells.attr("transform", d => `translate(${d.x0},${d.y0})`); this.cells .select("rect") .attr("width", d => d.x1 - d.x0) .attr("height", d => d.y1 - d.y0); this.labels .attr("x", d => (d.x1 - d.x0) / 2) .attr("y", d => (d.y1 - d.y0) / 2) .style("opacity", labelOpacity); } } class SunburstChart extends BaseChart { constructor(svg) { super(svg); this.margin = NO_MARGINS; this.x = linear$1().range([0, 2 * Math.PI]); this.y = sqrt(); this.partition = partition(); this.arc = arc() .startAngle(d => this.x(d.x0)) .endAngle(d => this.x(d.x1)) .innerRadius(d => this.y(d.y0)) .outerRadius(d => this.y(d.y1)); this.canvas = select$1(this.svg) .attr("class", "sunburst") .append("g") .on("mouseleave", () => this.mouseLeave()); // Bounding circle underneath the sunburst this.boundingCircle = this.canvas.append("circle").style("opacity", 0); this.accountLabel = this.canvas .append("text") .attr("class", "account") .attr("text-anchor", "middle"); this.balanceLabel = this.canvas .append("text") .attr("class", "balance") .attr("dy", "1.2em") .attr("text-anchor", "middle"); this.paths = this.canvas.selectAll("path"); } draw(data) { this.root = this.partition(data); this.paths = this.canvas .selectAll("path") .data(this.root.descendants()) .enter() .filter(d => !d.data.dummy && !!d.depth) .append("path") .attr("fill-rule", "evenodd") .style("fill", d => scales.sunburst(d.data.account)) .on("mouseover", d => this.mouseOver(d)) .call(makeAccountLink); this.update(); this.setLabel(this.root); return this; } update() { this.canvas.attr("transform", `translate(${this.width / 2 + this.margin.left},${this.height / 2 + this.margin.top})`); const radius = Math.min(this.width, this.height) / 2; this.boundingCircle.attr("r", radius); this.y.range([0, radius]); this.paths.attr("d", this.arc); } setLabel(d) { if (this.labelText) { this.balanceLabel.text(this.labelText(d)); } this.accountLabel .datum(d) .text(d.data.account) .call(makeAccountLink); } // Fade all but the current sequence mouseOver(d) { this.setLabel(d); this.paths.interrupt(); // Only highlight segments that are ancestors of the current segment. this.paths .style("opacity", 0.5) // check if d.account starts with node.account .filter(node => d.data.account.lastIndexOf(node.data.account, 0) === 0) .style("opacity", 1); } // Restore everything to full opacity when moving off the visualization. mouseLeave() { this.paths .transition() .duration(1000) .style("opacity", 1); if (this.root) { this.setLabel(this.root); } } } class SunburstChartContainer extends BaseChart { constructor(svg) { super(svg); this.svg.setAttribute("class", "sunburst"); this.sunbursts = []; this.canvases = []; this.margin = NO_MARGINS; this.currencies = []; this.setHeight(500); } draw(data) { this.currencies = Object.keys(data); this.currencies.forEach((currency, i) => { const canvas = select$1(this.svg) .append("g") .attr("transform", `translate(${(this.width * i) / this.currencies.length},0)`); const totalBalance = data[currency].value || 1; const sunburst = new SunburstChart(canvas.node()) .setWidth(this.width / this.currencies.length) .setHeight(500) .set("labelText", d => { const balance = d.value || 0; return `${formatCurrency(balance)} ${currency} (${formatPercentage(balance / totalBalance)})`; }) .draw(data[currency]); this.canvases.push(canvas); this.sunbursts.push(sunburst); }); return this; } update() { this.sunbursts.forEach((singleChart, i) => { singleChart .setWidth(this.width / this.currencies.length) .setHeight(500) .update(); this.canvases[i].attr("transform", `translate(${(this.width * i) / this.currencies.length},0)`); }); } } class HierarchyContainer extends BaseChart { constructor(svg) { super(svg); this.canvas = document.createElementNS("http://www.w3.org/2000/svg", "g"); svg.appendChild(this.canvas); this.has_mode_setting = true; this.margin = NO_MARGINS; this.currencies = []; this.currency = ""; this.mode = "treemap"; } draw(data) { this.data = data; this.currencies = Object.keys(data); this.canvas.innerHTML = ""; if (this.currencies.length === 0) { select$1(this.canvas) .append("text") .text("Chart is empty.") .attr("text-anchor", "middle") .attr("x", this.width / 2) .attr("y", 160 / 2); } else if (this.mode === "treemap") { if (!this.currency) { [this.currency] = this.currencies; } const totalBalance = data[this.currency].value || 1; const currentChart = new TreeMapChart(this.canvas) .setWidth(this.width) .set("tooltipText", d => { const balance = d.data.balance[this.currency]; return `${formatCurrency(balance)} ${this.currency} (${formatPercentage(balance / totalBalance)})${d.data.account}`; }) .draw(data[this.currency]); this.setHeight(currentChart.outerHeight); this.currentChart = currentChart; this.has_currency_setting = true; } else { this.currentChart = new SunburstChartContainer(this.canvas) .setWidth(this.width) .draw(data); this.setHeight(this.currentChart.outerHeight); this.has_currency_setting = false; } return this; } update() { if (!this.data) { return; } this.draw(this.data); if (this.currentChart) { this.currentChart.setWidth(this.outerWidth).update(); } } } /** * This module contains the main code to render Fava's charts. * * The charts heavily use d3 libraries. */ /** * The list of operating currencies, adding in the current conversion currency. */ let operatingCurrenciesWithConversion = []; conversion.subscribe(conversionValue => { if (!conversionValue || ["at_cost", "at_value", "units"].includes(conversionValue) || favaAPI.options.operating_currency.includes(conversionValue)) { operatingCurrenciesWithConversion = favaAPI.options.operating_currency; } else { operatingCurrenciesWithConversion = [ ...favaAPI.options.operating_currency, conversionValue, ]; } }); e.on("page-init", () => { const { accounts, options } = favaAPI; scales.treemap.domain(accounts); scales.sunburst.domain(accounts); options.operating_currency.sort(); options.commodities.sort(); scales.currencies.domain([ ...options.operating_currency, ...options.commodities, ]); }); const parsers = { balances(json) { const parsedData = array(object({ date, balance: record(number), }))(json); const allValues = []; for (const { date: date_, balance } of parsedData) { Object.entries(balance).forEach(([currency, value]) => { allValues.push({ name: currency, date: date_, value, }); }); } const data = [...group(allValues, v => v.name).entries()].map(([name, values]) => ({ name, values, })); return { data, renderer: (svg) => new LineChart(svg).set("tooltipText", d => `${formatCurrency(d.value)} ${d.name}${dateFormat.day(d.date)}`), }; }, commodities(json, label) { const parsedData = object({ quote: string, base: string, prices: array(tuple([date, number])), })(json); const renderer = (svg) => new LineChart(svg).set("tooltipText", d => `1 ${parsedData.base} = ${formatCurrency(d.value)} ${parsedData.quote}${dateFormat.day(d.date)}`); return { data: [ { name: label, values: parsedData.prices.map(d => ({ name: label, date: d[0], value: d[1], })), }, ], renderer, }; }, bar(json) { const jsonData = array(object({ date, budgets: record(number), balance: record(number) }))(json); const currentDateFmt = get_store_value(currentDateFormat); const data = jsonData.map(d => ({ values: operatingCurrenciesWithConversion.map(name => ({ name, value: d.balance[name] || 0, budget: d.budgets[name] || 0, })), date: d.date, label: currentDateFmt(d.date), })); const renderer = (svg) => new BarChart(svg).set("tooltipText", d => { let text = ""; d.values.forEach(a => { text += `${formatCurrency(a.value)} ${a.name}`; if (a.budget) { text += ` / ${formatCurrency(a.budget)} ${a.name}`; } text += "
"; }); text += `${d.label}`; return text; }); return { data, renderer }; }, hierarchy(json) { const hierarchyValidator = object({ account: string, balance: record(number), balance_children: record(number), children: lazy(() => array(hierarchyValidator)), }); const validator = object({ root: hierarchyValidator, modifier: number, }); const { root, modifier } = validator(json); addInternalNodesAsLeaves(root); const data = {}; operatingCurrenciesWithConversion.forEach(currency => { const currencyHierarchy = hierarchy(root) .sum(d => d.balance[currency] * modifier) .sort((a, b) => (b.value || 0) - (a.value || 0)); if (currencyHierarchy.value) { data[currency] = currencyHierarchy; } }); return { data, renderer: (svg) => new HierarchyContainer(svg), }; }, scatterplot(json) { const parser = array(object({ type: string, date, description: string, })); return { data: parser(json), renderer: (svg) => new ScatterPlot(svg), }; }, }; function parseChartData() { const chartData = array(object({ label: string, type: string, data: unknown, }))(getScriptTagJSON("#chart-data")); const result = []; chartData.forEach(chart => { const parser = parsers[chart.type]; if (parser) { result.push(Object.assign({ name: chart.label }, parser(chart.data, chart.label))); } }); return result; } function parseQueryChart(data) { if (!Array.isArray(data) || !data.length) { return undefined; } if (data[0].group !== undefined) { const validated = array(object({ group: string, balance: record(number) }))(data); const root = { account: "(root)", balance: {}, children: [], }; const accountMap = new Map([ [root.account, root], ]); const addNode = (node) => { const name = node.account; const existing = accountMap.get(name); if (existing) { existing.balance = node.balance; return; } accountMap.set(name, node); const parentEnd = name.lastIndexOf(":"); const parentId = parentEnd > 0 ? name.slice(0, parentEnd) : root.account; let parent = accountMap.get(parentId); if (!parent) { parent = { account: parentId, balance: {}, children: [] }; addNode(parent); } parent.children.push(node); }; for (const { group: account = "(empty)", balance } of validated) { addNode({ account, balance, children: [] }); } const chartData = {}; operatingCurrenciesWithConversion.forEach(currency => { const currencyHierarchy = hierarchy(root) .sum(d => d.balance[currency]) .sort((a, b) => (b.value || 0) - (a.value || 0)); if (currencyHierarchy.value !== undefined) { chartData[currency] = currencyHierarchy; } }); return { data: chartData, renderer: (svg) => new HierarchyContainer(svg), }; } if (data[0].date !== undefined) { return parsers.balances(data, ""); } return undefined; } // Copy the given text to the clipboard. function copyToClipboard(text) { if (!text) { return; } const textarea = document.createElement("textarea"); textarea.value = text; textarea.style.position = "fixed"; textarea.style.top = "0"; textarea.style.left = "0"; document.body.appendChild(textarea); textarea.focus(); textarea.select(); try { document.execCommand("copy"); } catch (err) { console.error("Unable to copy", err); // eslint-disable-line no-console } textarea.remove(); } e.on("page-loaded", () => { selectAll(".status-indicator").forEach(indicator => { indicator.addEventListener("click", () => { copyToClipboard(indicator.getAttribute("data-clipboard-text")); }); }); const copyBalances = select("#copy-balances"); if (copyBalances) { copyBalances.addEventListener("click", () => { copyToClipboard(copyBalances.getAttribute("data-clipboard-text")); }); } }); var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; function createCommonjsModule(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; } var codemirror = createCommonjsModule(function (module, exports) { // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE // This is CodeMirror (https://codemirror.net), a code editor // implemented in JavaScript on top of the browser's DOM. // // You can find some technical background for some of the code below // at http://marijnhaverbeke.nl/blog/#cm-internals . (function (global, factory) { module.exports = factory() ; }(commonjsGlobal, (function () { // Kludges for bugs and behavior differences that can't be feature // detected are enabled based on userAgent etc sniffing. var userAgent = navigator.userAgent; var platform = navigator.platform; var gecko = /gecko\/\d/i.test(userAgent); var ie_upto10 = /MSIE \d/.test(userAgent); var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent); var edge = /Edge\/(\d+)/.exec(userAgent); var ie = ie_upto10 || ie_11up || edge; var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]); var webkit = !edge && /WebKit\//.test(userAgent); var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent); var chrome = !edge && /Chrome\//.test(userAgent); var presto = /Opera\//.test(userAgent); var safari = /Apple Computer/.test(navigator.vendor); var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent); var phantom = /PhantomJS/.test(userAgent); var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent); var android = /Android/.test(userAgent); // This is woefully incomplete. Suggestions for alternative methods welcome. var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent); var mac = ios || /Mac/.test(platform); var chromeOS = /\bCrOS\b/.test(userAgent); var windows = /win/i.test(platform); var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/); if (presto_version) { presto_version = Number(presto_version[1]); } if (presto_version && presto_version >= 15) { presto = false; webkit = true; } // Some browsers use the wrong event properties to signal cmd/ctrl on OS X var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)); var captureRightClick = gecko || (ie && ie_version >= 9); function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } var rmClass = function(node, cls) { var current = node.className; var match = classTest(cls).exec(current); if (match) { var after = current.slice(match.index + match[0].length); node.className = current.slice(0, match.index) + (after ? match[1] + after : ""); } }; function removeChildren(e) { for (var count = e.childNodes.length; count > 0; --count) { e.removeChild(e.firstChild); } return e } function removeChildrenAndAdd(parent, e) { return removeChildren(parent).appendChild(e) } function elt(tag, content, className, style) { var e = document.createElement(tag); if (className) { e.className = className; } if (style) { e.style.cssText = style; } if (typeof content == "string") { e.appendChild(document.createTextNode(content)); } else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } } return e } // wrapper for elt, which removes the elt from the accessibility tree function eltP(tag, content, className, style) { var e = elt(tag, content, className, style); e.setAttribute("role", "presentation"); return e } var range; if (document.createRange) { range = function(node, start, end, endNode) { var r = document.createRange(); r.setEnd(endNode || node, end); r.setStart(node, start); return r }; } else { range = function(node, start, end) { var r = document.body.createTextRange(); try { r.moveToElementText(node.parentNode); } catch(e) { return r } r.collapse(true); r.moveEnd("character", end); r.moveStart("character", start); return r }; } function contains(parent, child) { if (child.nodeType == 3) // Android browser always returns false when child is a textnode { child = child.parentNode; } if (parent.contains) { return parent.contains(child) } do { if (child.nodeType == 11) { child = child.host; } if (child == parent) { return true } } while (child = child.parentNode) } function activeElt() { // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement. // IE < 10 will throw when accessed while the page is loading or in an iframe. // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable. var activeElement; try { activeElement = document.activeElement; } catch(e) { activeElement = document.body || null; } while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement) { activeElement = activeElement.shadowRoot.activeElement; } return activeElement } function addClass(node, cls) { var current = node.className; if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; } } function joinClasses(a, b) { var as = a.split(" "); for (var i = 0; i < as.length; i++) { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } } return b } var selectInput = function(node) { node.select(); }; if (ios) // Mobile Safari apparently has a bug where select() is broken. { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; } else if (ie) // Suppress mysterious IE10 errors { selectInput = function(node) { try { node.select(); } catch(_e) {} }; } function bind(f) { var args = Array.prototype.slice.call(arguments, 1); return function(){return f.apply(null, args)} } function copyObj(obj, target, overwrite) { if (!target) { target = {}; } for (var prop in obj) { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) { target[prop] = obj[prop]; } } return target } // Counts the column offset in a string, taking tabs into account. // Used mostly to find indentation. function countColumn(string, end, tabSize, startIndex, startValue) { if (end == null) { end = string.search(/[^\s\u00a0]/); if (end == -1) { end = string.length; } } for (var i = startIndex || 0, n = startValue || 0;;) { var nextTab = string.indexOf("\t", i); if (nextTab < 0 || nextTab >= end) { return n + (end - i) } n += nextTab - i; n += tabSize - (n % tabSize); i = nextTab + 1; } } var Delayed = function() { this.id = null; this.f = null; this.time = 0; this.handler = bind(this.onTimeout, this); }; Delayed.prototype.onTimeout = function (self) { self.id = 0; if (self.time <= +new Date) { self.f(); } else { setTimeout(self.handler, self.time - +new Date); } }; Delayed.prototype.set = function (ms, f) { this.f = f; var time = +new Date + ms; if (!this.id || time < this.time) { clearTimeout(this.id); this.id = setTimeout(this.handler, ms); this.time = time; } }; function indexOf(array, elt) { for (var i = 0; i < array.length; ++i) { if (array[i] == elt) { return i } } return -1 } // Number of pixels added to scroller and sizer to hide scrollbar var scrollerGap = 30; // Returned or thrown by various protocols to signal 'I'm not // handling this'. var Pass = {toString: function(){return "CodeMirror.Pass"}}; // Reused option objects for setSelection & friends var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"}; // The inverse of countColumn -- find the offset that corresponds to // a particular column. function findColumn(string, goal, tabSize) { for (var pos = 0, col = 0;;) { var nextTab = string.indexOf("\t", pos); if (nextTab == -1) { nextTab = string.length; } var skipped = nextTab - pos; if (nextTab == string.length || col + skipped >= goal) { return pos + Math.min(skipped, goal - col) } col += nextTab - pos; col += tabSize - (col % tabSize); pos = nextTab + 1; if (col >= goal) { return pos } } } var spaceStrs = [""]; function spaceStr(n) { while (spaceStrs.length <= n) { spaceStrs.push(lst(spaceStrs) + " "); } return spaceStrs[n] } function lst(arr) { return arr[arr.length-1] } function map(array, f) { var out = []; for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); } return out } function insertSorted(array, value, score) { var pos = 0, priority = score(value); while (pos < array.length && score(array[pos]) <= priority) { pos++; } array.splice(pos, 0, value); } function nothing() {} function createObj(base, props) { var inst; if (Object.create) { inst = Object.create(base); } else { nothing.prototype = base; inst = new nothing(); } if (props) { copyObj(props, inst); } return inst } var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; function isWordCharBasic(ch) { return /\w/.test(ch) || ch > "\x80" && (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)) } function isWordChar(ch, helper) { if (!helper) { return isWordCharBasic(ch) } if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true } return helper.test(ch) } function isEmpty(obj) { for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } } return true } // Extending unicode characters. A series of a non-extending char + // any number of extending chars is treated as a single unit as far // as editing and measuring is concerned. This is not fully correct, // since some scripts/fonts/browsers also treat other configurations // of code points as a group. var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/; function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) } // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range. function skipExtendingChars(str, pos, dir) { while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; } return pos } // Returns the value from the range [`from`; `to`] that satisfies // `pred` and is closest to `from`. Assumes that at least `to` // satisfies `pred`. Supports `from` being greater than `to`. function findFirst(pred, from, to) { // At any point we are certain `to` satisfies `pred`, don't know // whether `from` does. var dir = from > to ? -1 : 1; for (;;) { if (from == to) { return from } var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF); if (mid == from) { return pred(mid) ? from : to } if (pred(mid)) { to = mid; } else { from = mid + dir; } } } // BIDI HELPERS function iterateBidiSections(order, from, to, f) { if (!order) { return f(from, to, "ltr", 0) } var found = false; for (var i = 0; i < order.length; ++i) { var part = order[i]; if (part.from < to && part.to > from || from == to && part.to == from) { f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i); found = true; } } if (!found) { f(from, to, "ltr"); } } var bidiOther = null; function getBidiPartAt(order, ch, sticky) { var found; bidiOther = null; for (var i = 0; i < order.length; ++i) { var cur = order[i]; if (cur.from < ch && cur.to > ch) { return i } if (cur.to == ch) { if (cur.from != cur.to && sticky == "before") { found = i; } else { bidiOther = i; } } if (cur.from == ch) { if (cur.from != cur.to && sticky != "before") { found = i; } else { bidiOther = i; } } } return found != null ? found : bidiOther } // Bidirectional ordering algorithm // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm // that this (partially) implements. // One-char codes used for character types: // L (L): Left-to-Right // R (R): Right-to-Left // r (AL): Right-to-Left Arabic // 1 (EN): European Number // + (ES): European Number Separator // % (ET): European Number Terminator // n (AN): Arabic Number // , (CS): Common Number Separator // m (NSM): Non-Spacing Mark // b (BN): Boundary Neutral // s (B): Paragraph Separator // t (S): Segment Separator // w (WS): Whitespace // N (ON): Other Neutrals // Returns null if characters are ordered as they appear // (left-to-right), or an array of sections ({from, to, level} // objects) in the order in which they occur visually. var bidiOrdering = (function() { // Character types for codepoints 0 to 0xff var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"; // Character types for codepoints 0x600 to 0x6f9 var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"; function charType(code) { if (code <= 0xf7) { return lowTypes.charAt(code) } else if (0x590 <= code && code <= 0x5f4) { return "R" } else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) } else if (0x6ee <= code && code <= 0x8ac) { return "r" } else if (0x2000 <= code && code <= 0x200b) { return "w" } else if (code == 0x200c) { return "b" } else { return "L" } } var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/; var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/; function BidiSpan(level, from, to) { this.level = level; this.from = from; this.to = to; } return function(str, direction) { var outerType = direction == "ltr" ? "L" : "R"; if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false } var len = str.length, types = []; for (var i = 0; i < len; ++i) { types.push(charType(str.charCodeAt(i))); } // W1. Examine each non-spacing mark (NSM) in the level run, and // change the type of the NSM to the type of the previous // character. If the NSM is at the start of the level run, it will // get the type of sor. for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) { var type = types[i$1]; if (type == "m") { types[i$1] = prev; } else { prev = type; } } // W2. Search backwards from each instance of a European number // until the first strong type (R, L, AL, or sor) is found. If an // AL is found, change the type of the European number to Arabic // number. // W3. Change all ALs to R. for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) { var type$1 = types[i$2]; if (type$1 == "1" && cur == "r") { types[i$2] = "n"; } else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } } } // W4. A single European separator between two European numbers // changes to a European number. A single common separator between // two numbers of the same type changes to that type. for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) { var type$2 = types[i$3]; if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; } else if (type$2 == "," && prev$1 == types[i$3+1] && (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; } prev$1 = type$2; } // W5. A sequence of European terminators adjacent to European // numbers changes to all European numbers. // W6. Otherwise, separators and terminators change to Other // Neutral. for (var i$4 = 0; i$4 < len; ++i$4) { var type$3 = types[i$4]; if (type$3 == ",") { types[i$4] = "N"; } else if (type$3 == "%") { var end = (void 0); for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {} var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"; for (var j = i$4; j < end; ++j) { types[j] = replace; } i$4 = end - 1; } } // W7. Search backwards from each instance of a European number // until the first strong type (R, L, or sor) is found. If an L is // found, then change the type of the European number to L. for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) { var type$4 = types[i$5]; if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; } else if (isStrong.test(type$4)) { cur$1 = type$4; } } // N1. A sequence of neutrals takes the direction of the // surrounding strong text if the text on both sides has the same // direction. European and Arabic numbers act as if they were R in // terms of their influence on neutrals. Start-of-level-run (sor) // and end-of-level-run (eor) are used at level run boundaries. // N2. Any remaining neutrals take the embedding direction. for (var i$6 = 0; i$6 < len; ++i$6) { if (isNeutral.test(types[i$6])) { var end$1 = (void 0); for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {} var before = (i$6 ? types[i$6-1] : outerType) == "L"; var after = (end$1 < len ? types[end$1] : outerType) == "L"; var replace$1 = before == after ? (before ? "L" : "R") : outerType; for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; } i$6 = end$1 - 1; } } // Here we depart from the documented algorithm, in order to avoid // building up an actual levels array. Since there are only three // levels (0, 1, 2) in an implementation that doesn't take // explicit embedding into account, we can build up the order on // the fly, without following the level-based algorithm. var order = [], m; for (var i$7 = 0; i$7 < len;) { if (countsAsLeft.test(types[i$7])) { var start = i$7; for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {} order.push(new BidiSpan(0, start, i$7)); } else { var pos = i$7, at = order.length; for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {} for (var j$2 = pos; j$2 < i$7;) { if (countsAsNum.test(types[j$2])) { if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); } var nstart = j$2; for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {} order.splice(at, 0, new BidiSpan(2, nstart, j$2)); pos = j$2; } else { ++j$2; } } if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); } } } if (direction == "ltr") { if (order[0].level == 1 && (m = str.match(/^\s+/))) { order[0].from = m[0].length; order.unshift(new BidiSpan(0, 0, m[0].length)); } if (lst(order).level == 1 && (m = str.match(/\s+$/))) { lst(order).to -= m[0].length; order.push(new BidiSpan(0, len - m[0].length, len)); } } return direction == "rtl" ? order.reverse() : order } })(); // Get the bidi ordering for the given line (and cache it). Returns // false for lines that are fully left-to-right, and an array of // BidiSpan objects otherwise. function getOrder(line, direction) { var order = line.order; if (order == null) { order = line.order = bidiOrdering(line.text, direction); } return order } // EVENT HANDLING // Lightweight event framework. on/off also work on DOM nodes, // registering native DOM handlers. var noHandlers = []; var on = function(emitter, type, f) { if (emitter.addEventListener) { emitter.addEventListener(type, f, false); } else if (emitter.attachEvent) { emitter.attachEvent("on" + type, f); } else { var map = emitter._handlers || (emitter._handlers = {}); map[type] = (map[type] || noHandlers).concat(f); } }; function getHandlers(emitter, type) { return emitter._handlers && emitter._handlers[type] || noHandlers } function off(emitter, type, f) { if (emitter.removeEventListener) { emitter.removeEventListener(type, f, false); } else if (emitter.detachEvent) { emitter.detachEvent("on" + type, f); } else { var map = emitter._handlers, arr = map && map[type]; if (arr) { var index = indexOf(arr, f); if (index > -1) { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)); } } } } function signal(emitter, type /*, values...*/) { var handlers = getHandlers(emitter, type); if (!handlers.length) { return } var args = Array.prototype.slice.call(arguments, 2); for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); } } // The DOM events that CodeMirror handles can be overridden by // registering a (non-DOM) handler on the editor for the event name, // and preventDefault-ing the event in that handler. function signalDOMEvent(cm, e, override) { if (typeof e == "string") { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; } signal(cm, override || e.type, cm, e); return e_defaultPrevented(e) || e.codemirrorIgnore } function signalCursorActivity(cm) { var arr = cm._handlers && cm._handlers.cursorActivity; if (!arr) { return } var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []); for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1) { set.push(arr[i]); } } } function hasHandler(emitter, type) { return getHandlers(emitter, type).length > 0 } // Add on and off methods to a constructor's prototype, to make // registering events on such objects more convenient. function eventMixin(ctor) { ctor.prototype.on = function(type, f) {on(this, type, f);}; ctor.prototype.off = function(type, f) {off(this, type, f);}; } // Due to the fact that we still support jurassic IE versions, some // compatibility wrappers are needed. function e_preventDefault(e) { if (e.preventDefault) { e.preventDefault(); } else { e.returnValue = false; } } function e_stopPropagation(e) { if (e.stopPropagation) { e.stopPropagation(); } else { e.cancelBubble = true; } } function e_defaultPrevented(e) { return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false } function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);} function e_target(e) {return e.target || e.srcElement} function e_button(e) { var b = e.which; if (b == null) { if (e.button & 1) { b = 1; } else if (e.button & 2) { b = 3; } else if (e.button & 4) { b = 2; } } if (mac && e.ctrlKey && b == 1) { b = 3; } return b } // Detect drag-and-drop var dragAndDrop = function() { // There is *some* kind of drag-and-drop support in IE6-8, but I // couldn't get it to work yet. if (ie && ie_version < 9) { return false } var div = elt('div'); return "draggable" in div || "dragDrop" in div }(); var zwspSupported; function zeroWidthElement(measure) { if (zwspSupported == null) { var test = elt("span", "\u200b"); removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")])); if (measure.firstChild.offsetHeight != 0) { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); } } var node = zwspSupported ? elt("span", "\u200b") : elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px"); node.setAttribute("cm-text", ""); return node } // Feature-detect IE's crummy client rect reporting for bidi text var badBidiRects; function hasBadBidiRects(measure) { if (badBidiRects != null) { return badBidiRects } var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")); var r0 = range(txt, 0, 1).getBoundingClientRect(); var r1 = range(txt, 1, 2).getBoundingClientRect(); removeChildren(measure); if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780) return badBidiRects = (r1.right - r0.right < 3) } // See if "".split is the broken IE version, if so, provide an // alternative way to split lines. var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) { var pos = 0, result = [], l = string.length; while (pos <= l) { var nl = string.indexOf("\n", pos); if (nl == -1) { nl = string.length; } var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl); var rt = line.indexOf("\r"); if (rt != -1) { result.push(line.slice(0, rt)); pos += rt + 1; } else { result.push(line); pos = nl + 1; } } return result } : function (string) { return string.split(/\r\n?|\n/); }; var hasSelection = window.getSelection ? function (te) { try { return te.selectionStart != te.selectionEnd } catch(e) { return false } } : function (te) { var range; try {range = te.ownerDocument.selection.createRange();} catch(e) {} if (!range || range.parentElement() != te) { return false } return range.compareEndPoints("StartToEnd", range) != 0 }; var hasCopyEvent = (function () { var e = elt("div"); if ("oncopy" in e) { return true } e.setAttribute("oncopy", "return;"); return typeof e.oncopy == "function" })(); var badZoomedRects = null; function hasBadZoomedRects(measure) { if (badZoomedRects != null) { return badZoomedRects } var node = removeChildrenAndAdd(measure, elt("span", "x")); var normal = node.getBoundingClientRect(); var fromRange = range(node, 0, 1).getBoundingClientRect(); return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1 } // Known modes, by name and by MIME var modes = {}, mimeModes = {}; // Extra arguments are stored as the mode's dependencies, which is // used by (legacy) mechanisms like loadmode.js to automatically // load a mode. (Preferred mechanism is the require/define calls.) function defineMode(name, mode) { if (arguments.length > 2) { mode.dependencies = Array.prototype.slice.call(arguments, 2); } modes[name] = mode; } function defineMIME(mime, spec) { mimeModes[mime] = spec; } // Given a MIME type, a {name, ...options} config object, or a name // string, return a mode config object. function resolveMode(spec) { if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { spec = mimeModes[spec]; } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { var found = mimeModes[spec.name]; if (typeof found == "string") { found = {name: found}; } spec = createObj(found, spec); spec.name = found.name; } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { return resolveMode("application/xml") } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) { return resolveMode("application/json") } if (typeof spec == "string") { return {name: spec} } else { return spec || {name: "null"} } } // Given a mode spec (anything that resolveMode accepts), find and // initialize an actual mode object. function getMode(options, spec) { spec = resolveMode(spec); var mfactory = modes[spec.name]; if (!mfactory) { return getMode(options, "text/plain") } var modeObj = mfactory(options, spec); if (modeExtensions.hasOwnProperty(spec.name)) { var exts = modeExtensions[spec.name]; for (var prop in exts) { if (!exts.hasOwnProperty(prop)) { continue } if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; } modeObj[prop] = exts[prop]; } } modeObj.name = spec.name; if (spec.helperType) { modeObj.helperType = spec.helperType; } if (spec.modeProps) { for (var prop$1 in spec.modeProps) { modeObj[prop$1] = spec.modeProps[prop$1]; } } return modeObj } // This can be used to attach properties to mode objects from // outside the actual mode definition. var modeExtensions = {}; function extendMode(mode, properties) { var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); copyObj(properties, exts); } function copyState(mode, state) { if (state === true) { return state } if (mode.copyState) { return mode.copyState(state) } var nstate = {}; for (var n in state) { var val = state[n]; if (val instanceof Array) { val = val.concat([]); } nstate[n] = val; } return nstate } // Given a mode and a state (for that mode), find the inner mode and // state at the position that the state refers to. function innerMode(mode, state) { var info; while (mode.innerMode) { info = mode.innerMode(state); if (!info || info.mode == mode) { break } state = info.state; mode = info.mode; } return info || {mode: mode, state: state} } function startState(mode, a1, a2) { return mode.startState ? mode.startState(a1, a2) : true } // STRING STREAM // Fed to the mode parsers, provides helper functions to make // parsers more succinct. var StringStream = function(string, tabSize, lineOracle) { this.pos = this.start = 0; this.string = string; this.tabSize = tabSize || 8; this.lastColumnPos = this.lastColumnValue = 0; this.lineStart = 0; this.lineOracle = lineOracle; }; StringStream.prototype.eol = function () {return this.pos >= this.string.length}; StringStream.prototype.sol = function () {return this.pos == this.lineStart}; StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined}; StringStream.prototype.next = function () { if (this.pos < this.string.length) { return this.string.charAt(this.pos++) } }; StringStream.prototype.eat = function (match) { var ch = this.string.charAt(this.pos); var ok; if (typeof match == "string") { ok = ch == match; } else { ok = ch && (match.test ? match.test(ch) : match(ch)); } if (ok) {++this.pos; return ch} }; StringStream.prototype.eatWhile = function (match) { var start = this.pos; while (this.eat(match)){} return this.pos > start }; StringStream.prototype.eatSpace = function () { var start = this.pos; while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; } return this.pos > start }; StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;}; StringStream.prototype.skipTo = function (ch) { var found = this.string.indexOf(ch, this.pos); if (found > -1) {this.pos = found; return true} }; StringStream.prototype.backUp = function (n) {this.pos -= n;}; StringStream.prototype.column = function () { if (this.lastColumnPos < this.start) { this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue); this.lastColumnPos = this.start; } return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) }; StringStream.prototype.indentation = function () { return countColumn(this.string, null, this.tabSize) - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) }; StringStream.prototype.match = function (pattern, consume, caseInsensitive) { if (typeof pattern == "string") { var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }; var substr = this.string.substr(this.pos, pattern.length); if (cased(substr) == cased(pattern)) { if (consume !== false) { this.pos += pattern.length; } return true } } else { var match = this.string.slice(this.pos).match(pattern); if (match && match.index > 0) { return null } if (match && consume !== false) { this.pos += match[0].length; } return match } }; StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)}; StringStream.prototype.hideFirstChars = function (n, inner) { this.lineStart += n; try { return inner() } finally { this.lineStart -= n; } }; StringStream.prototype.lookAhead = function (n) { var oracle = this.lineOracle; return oracle && oracle.lookAhead(n) }; StringStream.prototype.baseToken = function () { var oracle = this.lineOracle; return oracle && oracle.baseToken(this.pos) }; // Find the line object corresponding to the given line number. function getLine(doc, n) { n -= doc.first; if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") } var chunk = doc; while (!chunk.lines) { for (var i = 0;; ++i) { var child = chunk.children[i], sz = child.chunkSize(); if (n < sz) { chunk = child; break } n -= sz; } } return chunk.lines[n] } // Get the part of a document between two positions, as an array of // strings. function getBetween(doc, start, end) { var out = [], n = start.line; doc.iter(start.line, end.line + 1, function (line) { var text = line.text; if (n == end.line) { text = text.slice(0, end.ch); } if (n == start.line) { text = text.slice(start.ch); } out.push(text); ++n; }); return out } // Get the lines between from and to, as array of strings. function getLines(doc, from, to) { var out = []; doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value return out } // Update the height of a line, propagating the height change // upwards to parent nodes. function updateLineHeight(line, height) { var diff = height - line.height; if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } } } // Given a line object, find its line number by walking up through // its parent links. function lineNo(line) { if (line.parent == null) { return null } var cur = line.parent, no = indexOf(cur.lines, line); for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { for (var i = 0;; ++i) { if (chunk.children[i] == cur) { break } no += chunk.children[i].chunkSize(); } } return no + cur.first } // Find the line at the given vertical position, using the height // information in the document tree. function lineAtHeight(chunk, h) { var n = chunk.first; outer: do { for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) { var child = chunk.children[i$1], ch = child.height; if (h < ch) { chunk = child; continue outer } h -= ch; n += child.chunkSize(); } return n } while (!chunk.lines) var i = 0; for (; i < chunk.lines.length; ++i) { var line = chunk.lines[i], lh = line.height; if (h < lh) { break } h -= lh; } return n + i } function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size} function lineNumberFor(options, i) { return String(options.lineNumberFormatter(i + options.firstLineNumber)) } // A Pos instance represents a position within the text. function Pos(line, ch, sticky) { if ( sticky === void 0 ) sticky = null; if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) } this.line = line; this.ch = ch; this.sticky = sticky; } // Compare two positions, return 0 if they are the same, a negative // number when a is less, and a positive number otherwise. function cmp(a, b) { return a.line - b.line || a.ch - b.ch } function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 } function copyPos(x) {return Pos(x.line, x.ch)} function maxPos(a, b) { return cmp(a, b) < 0 ? b : a } function minPos(a, b) { return cmp(a, b) < 0 ? a : b } // Most of the external API clips given positions to make sure they // actually exist within the document. function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))} function clipPos(doc, pos) { if (pos.line < doc.first) { return Pos(doc.first, 0) } var last = doc.first + doc.size - 1; if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) } return clipToLen(pos, getLine(doc, pos.line).text.length) } function clipToLen(pos, linelen) { var ch = pos.ch; if (ch == null || ch > linelen) { return Pos(pos.line, linelen) } else if (ch < 0) { return Pos(pos.line, 0) } else { return pos } } function clipPosArray(doc, array) { var out = []; for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); } return out } var SavedContext = function(state, lookAhead) { this.state = state; this.lookAhead = lookAhead; }; var Context = function(doc, state, line, lookAhead) { this.state = state; this.doc = doc; this.line = line; this.maxLookAhead = lookAhead || 0; this.baseTokens = null; this.baseTokenPos = 1; }; Context.prototype.lookAhead = function (n) { var line = this.doc.getLine(this.line + n); if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; } return line }; Context.prototype.baseToken = function (n) { if (!this.baseTokens) { return null } while (this.baseTokens[this.baseTokenPos] <= n) { this.baseTokenPos += 2; } var type = this.baseTokens[this.baseTokenPos + 1]; return {type: type && type.replace(/( |^)overlay .*/, ""), size: this.baseTokens[this.baseTokenPos] - n} }; Context.prototype.nextLine = function () { this.line++; if (this.maxLookAhead > 0) { this.maxLookAhead--; } }; Context.fromSaved = function (doc, saved, line) { if (saved instanceof SavedContext) { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) } else { return new Context(doc, copyState(doc.mode, saved), line) } }; Context.prototype.save = function (copy) { var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state; return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state }; // Compute a style array (an array starting with a mode generation // -- for invalidation -- followed by pairs of end positions and // style strings), which is used to highlight the tokens on the // line. function highlightLine(cm, line, context, forceToEnd) { // A styles array always starts with a number identifying the // mode/overlays that it is based on (for easy invalidation). var st = [cm.state.modeGen], lineClasses = {}; // Compute the base array of styles runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); }, lineClasses, forceToEnd); var state = context.state; // Run overlays, adjust style array. var loop = function ( o ) { context.baseTokens = st; var overlay = cm.state.overlays[o], i = 1, at = 0; context.state = true; runMode(cm, line.text, overlay.mode, context, function (end, style) { var start = i; // Ensure there's a token end at the current position, and that i points at it while (at < end) { var i_end = st[i]; if (i_end > end) { st.splice(i, 1, end, st[i+1], i_end); } i += 2; at = Math.min(end, i_end); } if (!style) { return } if (overlay.opaque) { st.splice(start, i - start, end, "overlay " + style); i = start + 2; } else { for (; start < i; start += 2) { var cur = st[start+1]; st[start+1] = (cur ? cur + " " : "") + "overlay " + style; } } }, lineClasses); context.state = state; context.baseTokens = null; context.baseTokenPos = 1; }; for (var o = 0; o < cm.state.overlays.length; ++o) loop( o ); return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null} } function getLineStyles(cm, line, updateFrontier) { if (!line.styles || line.styles[0] != cm.state.modeGen) { var context = getContextBefore(cm, lineNo(line)); var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state); var result = highlightLine(cm, line, context); if (resetState) { context.state = resetState; } line.stateAfter = context.save(!resetState); line.styles = result.styles; if (result.classes) { line.styleClasses = result.classes; } else if (line.styleClasses) { line.styleClasses = null; } if (updateFrontier === cm.doc.highlightFrontier) { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); } } return line.styles } function getContextBefore(cm, n, precise) { var doc = cm.doc, display = cm.display; if (!doc.mode.startState) { return new Context(doc, true, n) } var start = findStartLine(cm, n, precise); var saved = start > doc.first && getLine(doc, start - 1).stateAfter; var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start); doc.iter(start, n, function (line) { processLine(cm, line.text, context); var pos = context.line; line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null; context.nextLine(); }); if (precise) { doc.modeFrontier = context.line; } return context } // Lightweight form of highlight -- proceed over this line and // update state, but don't save a style array. Used for lines that // aren't currently visible. function processLine(cm, text, context, startAt) { var mode = cm.doc.mode; var stream = new StringStream(text, cm.options.tabSize, context); stream.start = stream.pos = startAt || 0; if (text == "") { callBlankLine(mode, context.state); } while (!stream.eol()) { readToken(mode, stream, context.state); stream.start = stream.pos; } } function callBlankLine(mode, state) { if (mode.blankLine) { return mode.blankLine(state) } if (!mode.innerMode) { return } var inner = innerMode(mode, state); if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) } } function readToken(mode, stream, state, inner) { for (var i = 0; i < 10; i++) { if (inner) { inner[0] = innerMode(mode, state).mode; } var style = mode.token(stream, state); if (stream.pos > stream.start) { return style } } throw new Error("Mode " + mode.name + " failed to advance stream.") } var Token = function(stream, type, state) { this.start = stream.start; this.end = stream.pos; this.string = stream.current(); this.type = type || null; this.state = state; }; // Utility for getTokenAt and getLineTokens function takeToken(cm, pos, precise, asArray) { var doc = cm.doc, mode = doc.mode, style; pos = clipPos(doc, pos); var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise); var stream = new StringStream(line.text, cm.options.tabSize, context), tokens; if (asArray) { tokens = []; } while ((asArray || stream.pos < pos.ch) && !stream.eol()) { stream.start = stream.pos; style = readToken(mode, stream, context.state); if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); } } return asArray ? tokens : new Token(stream, style, context.state) } function extractLineClasses(type, output) { if (type) { for (;;) { var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/); if (!lineClass) { break } type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length); var prop = lineClass[1] ? "bgClass" : "textClass"; if (output[prop] == null) { output[prop] = lineClass[2]; } else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop])) { output[prop] += " " + lineClass[2]; } } } return type } // Run the given mode's parser over a line, calling f for each token. function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) { var flattenSpans = mode.flattenSpans; if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; } var curStart = 0, curStyle = null; var stream = new StringStream(text, cm.options.tabSize, context), style; var inner = cm.options.addModeClass && [null]; if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); } while (!stream.eol()) { if (stream.pos > cm.options.maxHighlightLength) { flattenSpans = false; if (forceToEnd) { processLine(cm, text, context, stream.pos); } stream.pos = text.length; style = null; } else { style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses); } if (inner) { var mName = inner[0].name; if (mName) { style = "m-" + (style ? mName + " " + style : mName); } } if (!flattenSpans || curStyle != style) { while (curStart < stream.start) { curStart = Math.min(stream.start, curStart + 5000); f(curStart, curStyle); } curStyle = style; } stream.start = stream.pos; } while (curStart < stream.pos) { // Webkit seems to refuse to render text nodes longer than 57444 // characters, and returns inaccurate measurements in nodes // starting around 5000 chars. var pos = Math.min(stream.pos, curStart + 5000); f(pos, curStyle); curStart = pos; } } // Finds the line to start with when starting a parse. Tries to // find a line with a stateAfter, so that it can start with a // valid state. If that fails, it returns the line with the // smallest indentation, which tends to need the least context to // parse correctly. function findStartLine(cm, n, precise) { var minindent, minline, doc = cm.doc; var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100); for (var search = n; search > lim; --search) { if (search <= doc.first) { return doc.first } var line = getLine(doc, search - 1), after = line.stateAfter; if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier)) { return search } var indented = countColumn(line.text, null, cm.options.tabSize); if (minline == null || minindent > indented) { minline = search - 1; minindent = indented; } } return minline } function retreatFrontier(doc, n) { doc.modeFrontier = Math.min(doc.modeFrontier, n); if (doc.highlightFrontier < n - 10) { return } var start = doc.first; for (var line = n - 1; line > start; line--) { var saved = getLine(doc, line).stateAfter; // change is on 3 // state on line 1 looked ahead 2 -- so saw 3 // test 1 + 2 < 3 should cover this if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) { start = line + 1; break } } doc.highlightFrontier = Math.min(doc.highlightFrontier, start); } // Optimize some code when these features are not used. var sawReadOnlySpans = false, sawCollapsedSpans = false; function seeReadOnlySpans() { sawReadOnlySpans = true; } function seeCollapsedSpans() { sawCollapsedSpans = true; } // TEXTMARKER SPANS function MarkedSpan(marker, from, to) { this.marker = marker; this.from = from; this.to = to; } // Search an array of spans for a span matching the given marker. function getMarkedSpanFor(spans, marker) { if (spans) { for (var i = 0; i < spans.length; ++i) { var span = spans[i]; if (span.marker == marker) { return span } } } } // Remove a span from an array, returning undefined if no spans are // left (we don't store arrays for lines without spans). function removeMarkedSpan(spans, span) { var r; for (var i = 0; i < spans.length; ++i) { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } } return r } // Add a span to a line. function addMarkedSpan(line, span) { line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]; span.marker.attachLine(line); } // Used for the algorithm that adjusts markers for a change in the // document. These functions cut an array of spans at a given // character position, returning an array of remaining chunks (or // undefined if nothing remains). function markedSpansBefore(old, startCh, isInsert) { var nw; if (old) { for (var i = 0; i < old.length; ++i) { var span = old[i], marker = span.marker; var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh); if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh) ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)); } } } return nw } function markedSpansAfter(old, endCh, isInsert) { var nw; if (old) { for (var i = 0; i < old.length; ++i) { var span = old[i], marker = span.marker; var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh); if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh) ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, span.to == null ? null : span.to - endCh)); } } } return nw } // Given a change object, compute the new set of marker spans that // cover the line in which the change took place. Removes spans // entirely within the change, reconnects spans belonging to the // same marker that appear on both sides of the change, and cuts off // spans partially within the change. Returns an array of span // arrays with one element for each line in (after) the change. function stretchSpansOverChange(doc, change) { if (change.full) { return null } var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans; var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans; if (!oldFirst && !oldLast) { return null } var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0; // Get the spans that 'stick out' on both sides var first = markedSpansBefore(oldFirst, startCh, isInsert); var last = markedSpansAfter(oldLast, endCh, isInsert); // Next, merge those two ends var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0); if (first) { // Fix up .to properties of first for (var i = 0; i < first.length; ++i) { var span = first[i]; if (span.to == null) { var found = getMarkedSpanFor(last, span.marker); if (!found) { span.to = startCh; } else if (sameLine) { span.to = found.to == null ? null : found.to + offset; } } } } if (last) { // Fix up .from in last (or move them into first in case of sameLine) for (var i$1 = 0; i$1 < last.length; ++i$1) { var span$1 = last[i$1]; if (span$1.to != null) { span$1.to += offset; } if (span$1.from == null) { var found$1 = getMarkedSpanFor(first, span$1.marker); if (!found$1) { span$1.from = offset; if (sameLine) { (first || (first = [])).push(span$1); } } } else { span$1.from += offset; if (sameLine) { (first || (first = [])).push(span$1); } } } } // Make sure we didn't create any zero-length spans if (first) { first = clearEmptySpans(first); } if (last && last != first) { last = clearEmptySpans(last); } var newMarkers = [first]; if (!sameLine) { // Fill gap with whole-line-spans var gap = change.text.length - 2, gapMarkers; if (gap > 0 && first) { for (var i$2 = 0; i$2 < first.length; ++i$2) { if (first[i$2].to == null) { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } } for (var i$3 = 0; i$3 < gap; ++i$3) { newMarkers.push(gapMarkers); } newMarkers.push(last); } return newMarkers } // Remove spans that are empty and don't have a clearWhenEmpty // option of false. function clearEmptySpans(spans) { for (var i = 0; i < spans.length; ++i) { var span = spans[i]; if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) { spans.splice(i--, 1); } } if (!spans.length) { return null } return spans } // Used to 'clip' out readOnly ranges when making a change. function removeReadOnlyRanges(doc, from, to) { var markers = null; doc.iter(from.line, to.line + 1, function (line) { if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { var mark = line.markedSpans[i].marker; if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) { (markers || (markers = [])).push(mark); } } } }); if (!markers) { return null } var parts = [{from: from, to: to}]; for (var i = 0; i < markers.length; ++i) { var mk = markers[i], m = mk.find(0); for (var j = 0; j < parts.length; ++j) { var p = parts[j]; if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue } var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to); if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) { newParts.push({from: p.from, to: m.from}); } if (dto > 0 || !mk.inclusiveRight && !dto) { newParts.push({from: m.to, to: p.to}); } parts.splice.apply(parts, newParts); j += newParts.length - 3; } } return parts } // Connect or disconnect spans from a line. function detachMarkedSpans(line) { var spans = line.markedSpans; if (!spans) { return } for (var i = 0; i < spans.length; ++i) { spans[i].marker.detachLine(line); } line.markedSpans = null; } function attachMarkedSpans(line, spans) { if (!spans) { return } for (var i = 0; i < spans.length; ++i) { spans[i].marker.attachLine(line); } line.markedSpans = spans; } // Helpers used when computing which overlapping collapsed span // counts as the larger one. function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 } function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 } // Returns a number indicating which of two overlapping collapsed // spans is larger (and thus includes the other). Falls back to // comparing ids when the spans cover exactly the same range. function compareCollapsedMarkers(a, b) { var lenDiff = a.lines.length - b.lines.length; if (lenDiff != 0) { return lenDiff } var aPos = a.find(), bPos = b.find(); var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b); if (fromCmp) { return -fromCmp } var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b); if (toCmp) { return toCmp } return b.id - a.id } // Find out whether a line ends or starts in a collapsed span. If // so, return the marker for that span. function collapsedSpanAtSide(line, start) { var sps = sawCollapsedSpans && line.markedSpans, found; if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { sp = sps[i]; if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; } } } return found } function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) } function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) } function collapsedSpanAround(line, ch) { var sps = sawCollapsedSpans && line.markedSpans, found; if (sps) { for (var i = 0; i < sps.length; ++i) { var sp = sps[i]; if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) && (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; } } } return found } // Test whether there exists a collapsed span that partially // overlaps (covers the start or end, but not both) of a new span. // Such overlap is not allowed. function conflictingCollapsedRange(doc, lineNo, from, to, marker) { var line = getLine(doc, lineNo); var sps = sawCollapsedSpans && line.markedSpans; if (sps) { for (var i = 0; i < sps.length; ++i) { var sp = sps[i]; if (!sp.marker.collapsed) { continue } var found = sp.marker.find(0); var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker); var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker); if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue } if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) || fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0)) { return true } } } } // A visual line is a line as drawn on the screen. Folding, for // example, can cause multiple logical lines to appear on the same // visual line. This finds the start of the visual line that the // given line is part of (usually that is the line itself). function visualLine(line) { var merged; while (merged = collapsedSpanAtStart(line)) { line = merged.find(-1, true).line; } return line } function visualLineEnd(line) { var merged; while (merged = collapsedSpanAtEnd(line)) { line = merged.find(1, true).line; } return line } // Returns an array of logical lines that continue the visual line // started by the argument, or undefined if there are no such lines. function visualLineContinued(line) { var merged, lines; while (merged = collapsedSpanAtEnd(line)) { line = merged.find(1, true).line ;(lines || (lines = [])).push(line); } return lines } // Get the line number of the start of the visual line that the // given line number is part of. function visualLineNo(doc, lineN) { var line = getLine(doc, lineN), vis = visualLine(line); if (line == vis) { return lineN } return lineNo(vis) } // Get the line number of the start of the next visual line after // the given line. function visualLineEndNo(doc, lineN) { if (lineN > doc.lastLine()) { return lineN } var line = getLine(doc, lineN), merged; if (!lineIsHidden(doc, line)) { return lineN } while (merged = collapsedSpanAtEnd(line)) { line = merged.find(1, true).line; } return lineNo(line) + 1 } // Compute whether a line is hidden. Lines count as hidden when they // are part of a visual line that starts with another line, or when // they are entirely covered by collapsed, non-widget span. function lineIsHidden(doc, line) { var sps = sawCollapsedSpans && line.markedSpans; if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { sp = sps[i]; if (!sp.marker.collapsed) { continue } if (sp.from == null) { return true } if (sp.marker.widgetNode) { continue } if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) { return true } } } } function lineIsHiddenInner(doc, line, span) { if (span.to == null) { var end = span.marker.find(1, true); return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)) } if (span.marker.inclusiveRight && span.to == line.text.length) { return true } for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) { sp = line.markedSpans[i]; if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to && (sp.to == null || sp.to != span.from) && (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && lineIsHiddenInner(doc, line, sp)) { return true } } } // Find the height above the given line. function heightAtLine(lineObj) { lineObj = visualLine(lineObj); var h = 0, chunk = lineObj.parent; for (var i = 0; i < chunk.lines.length; ++i) { var line = chunk.lines[i]; if (line == lineObj) { break } else { h += line.height; } } for (var p = chunk.parent; p; chunk = p, p = chunk.parent) { for (var i$1 = 0; i$1 < p.children.length; ++i$1) { var cur = p.children[i$1]; if (cur == chunk) { break } else { h += cur.height; } } } return h } // Compute the character length of a line, taking into account // collapsed ranges (see markText) that might hide parts, and join // other lines onto it. function lineLength(line) { if (line.height == 0) { return 0 } var len = line.text.length, merged, cur = line; while (merged = collapsedSpanAtStart(cur)) { var found = merged.find(0, true); cur = found.from.line; len += found.from.ch - found.to.ch; } cur = line; while (merged = collapsedSpanAtEnd(cur)) { var found$1 = merged.find(0, true); len -= cur.text.length - found$1.from.ch; cur = found$1.to.line; len += cur.text.length - found$1.to.ch; } return len } // Find the longest line in the document. function findMaxLine(cm) { var d = cm.display, doc = cm.doc; d.maxLine = getLine(doc, doc.first); d.maxLineLength = lineLength(d.maxLine); d.maxLineChanged = true; doc.iter(function (line) { var len = lineLength(line); if (len > d.maxLineLength) { d.maxLineLength = len; d.maxLine = line; } }); } // LINE DATA STRUCTURE // Line objects. These hold state related to a line, including // highlighting info (the styles array). var Line = function(text, markedSpans, estimateHeight) { this.text = text; attachMarkedSpans(this, markedSpans); this.height = estimateHeight ? estimateHeight(this) : 1; }; Line.prototype.lineNo = function () { return lineNo(this) }; eventMixin(Line); // Change the content (text, markers) of a line. Automatically // invalidates cached information and tries to re-estimate the // line's height. function updateLine(line, text, markedSpans, estimateHeight) { line.text = text; if (line.stateAfter) { line.stateAfter = null; } if (line.styles) { line.styles = null; } if (line.order != null) { line.order = null; } detachMarkedSpans(line); attachMarkedSpans(line, markedSpans); var estHeight = estimateHeight ? estimateHeight(line) : 1; if (estHeight != line.height) { updateLineHeight(line, estHeight); } } // Detach a line from the document tree and its markers. function cleanUpLine(line) { line.parent = null; detachMarkedSpans(line); } // Convert a style as returned by a mode (either null, or a string // containing one or more styles) to a CSS style. This is cached, // and also looks for line-wide styles. var styleToClassCache = {}, styleToClassCacheWithMode = {}; function interpretTokenStyle(style, options) { if (!style || /^\s*$/.test(style)) { return null } var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache; return cache[style] || (cache[style] = style.replace(/\S+/g, "cm-$&")) } // Render the DOM representation of the text of a line. Also builds // up a 'line map', which points at the DOM nodes that represent // specific stretches of text, and is used by the measuring code. // The returned object contains the DOM node, this map, and // information about line-wide styles that were set by the mode. function buildLineContent(cm, lineView) { // The padding-right forces the element to have a 'border', which // is needed on Webkit to be able to get line-level bounding // rectangles for it (in measureChar). var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null); var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content, col: 0, pos: 0, cm: cm, trailingSpace: false, splitSpaces: cm.getOption("lineWrapping")}; lineView.measure = {}; // Iterate over the logical lines that make up this visual line. for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0); builder.pos = 0; builder.addToken = buildToken; // Optionally wire in some hacks into the token-rendering // algorithm, to deal with browser quirks. if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction))) { builder.addToken = buildTokenBadBidi(builder.addToken, order); } builder.map = []; var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line); insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)); if (line.styleClasses) { if (line.styleClasses.bgClass) { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); } if (line.styleClasses.textClass) { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); } } // Ensure at least a single node is present, for measuring. if (builder.map.length == 0) { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); } // Store the map and a cache object for the current logical line if (i == 0) { lineView.measure.map = builder.map; lineView.measure.cache = {}; } else { (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map) ;(lineView.measure.caches || (lineView.measure.caches = [])).push({}); } } // See issue #2901 if (webkit) { var last = builder.content.lastChild; if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab"))) { builder.content.className = "cm-tab-wrap-hack"; } } signal(cm, "renderLine", cm, lineView.line, builder.pre); if (builder.pre.className) { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); } return builder } function defaultSpecialCharPlaceholder(ch) { var token = elt("span", "\u2022", "cm-invalidchar"); token.title = "\\u" + ch.charCodeAt(0).toString(16); token.setAttribute("aria-label", token.title); return token } // Build up the DOM representation for a single token, and add it to // the line map. Takes care to render special characters separately. function buildToken(builder, text, style, startStyle, endStyle, css, attributes) { if (!text) { return } var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text; var special = builder.cm.state.specialChars, mustWrap = false; var content; if (!special.test(text)) { builder.col += text.length; content = document.createTextNode(displayText); builder.map.push(builder.pos, builder.pos + text.length, content); if (ie && ie_version < 9) { mustWrap = true; } builder.pos += text.length; } else { content = document.createDocumentFragment(); var pos = 0; while (true) { special.lastIndex = pos; var m = special.exec(text); var skipped = m ? m.index - pos : text.length - pos; if (skipped) { var txt = document.createTextNode(displayText.slice(pos, pos + skipped)); if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); } else { content.appendChild(txt); } builder.map.push(builder.pos, builder.pos + skipped, txt); builder.col += skipped; builder.pos += skipped; } if (!m) { break } pos += skipped + 1; var txt$1 = (void 0); if (m[0] == "\t") { var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize; txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); txt$1.setAttribute("role", "presentation"); txt$1.setAttribute("cm-text", "\t"); builder.col += tabWidth; } else if (m[0] == "\r" || m[0] == "\n") { txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")); txt$1.setAttribute("cm-text", m[0]); builder.col += 1; } else { txt$1 = builder.cm.options.specialCharPlaceholder(m[0]); txt$1.setAttribute("cm-text", m[0]); if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); } else { content.appendChild(txt$1); } builder.col += 1; } builder.map.push(builder.pos, builder.pos + 1, txt$1); builder.pos++; } } builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32; if (style || startStyle || endStyle || mustWrap || css) { var fullStyle = style || ""; if (startStyle) { fullStyle += startStyle; } if (endStyle) { fullStyle += endStyle; } var token = elt("span", [content], fullStyle, css); if (attributes) { for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class") { token.setAttribute(attr, attributes[attr]); } } } return builder.content.appendChild(token) } builder.content.appendChild(content); } // Change some spaces to NBSP to prevent the browser from collapsing // trailing spaces at the end of a line when rendering text (issue #1362). function splitSpaces(text, trailingBefore) { if (text.length > 1 && !/ /.test(text)) { return text } var spaceBefore = trailingBefore, result = ""; for (var i = 0; i < text.length; i++) { var ch = text.charAt(i); if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32)) { ch = "\u00a0"; } result += ch; spaceBefore = ch == " "; } return result } // Work around nonsense dimensions being reported for stretches of // right-to-left text. function buildTokenBadBidi(inner, order) { return function (builder, text, style, startStyle, endStyle, css, attributes) { style = style ? style + " cm-force-border" : "cm-force-border"; var start = builder.pos, end = start + text.length; for (;;) { // Find the part that overlaps with the start of this text var part = (void 0); for (var i = 0; i < order.length; i++) { part = order[i]; if (part.to > start && part.from <= start) { break } } if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) } inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes); startStyle = null; text = text.slice(part.to - start); start = part.to; } } } function buildCollapsedSpan(builder, size, marker, ignoreWidget) { var widget = !ignoreWidget && marker.widgetNode; if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); } if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { if (!widget) { widget = builder.content.appendChild(document.createElement("span")); } widget.setAttribute("cm-marker", marker.id); } if (widget) { builder.cm.display.input.setUneditable(widget); builder.content.appendChild(widget); } builder.pos += size; builder.trailingSpace = false; } // Outputs a number of spans to make up a line, taking highlighting // and marked text into account. function insertLineContent(line, builder, styles) { var spans = line.markedSpans, allText = line.text, at = 0; if (!spans) { for (var i$1 = 1; i$1 < styles.length; i$1+=2) { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); } return } var len = allText.length, pos = 0, i = 1, text = "", style, css; var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes; for (;;) { if (nextChange == pos) { // Update current marker set spanStyle = spanEndStyle = spanStartStyle = css = ""; attributes = null; collapsed = null; nextChange = Infinity; var foundBookmarks = [], endStyles = (void 0); for (var j = 0; j < spans.length; ++j) { var sp = spans[j], m = sp.marker; if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { foundBookmarks.push(m); } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) { if (sp.to != null && sp.to != pos && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; } if (m.className) { spanStyle += " " + m.className; } if (m.css) { css = (css ? css + ";" : "") + m.css; } if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; } if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); } // support for the old title property // https://github.com/codemirror/CodeMirror/pull/5673 if (m.title) { (attributes || (attributes = {})).title = m.title; } if (m.attributes) { for (var attr in m.attributes) { (attributes || (attributes = {}))[attr] = m.attributes[attr]; } } if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) { collapsed = sp; } } else if (sp.from > pos && nextChange > sp.from) { nextChange = sp.from; } } if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2) { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } } if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2) { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } } if (collapsed && (collapsed.from || 0) == pos) { buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, collapsed.marker, collapsed.from == null); if (collapsed.to == null) { return } if (collapsed.to == pos) { collapsed = false; } } } if (pos >= len) { break } var upto = Math.min(len, nextChange); while (true) { if (text) { var end = pos + text.length; if (!collapsed) { var tokenText = end > upto ? text.slice(0, upto - pos) : text; builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes); } if (end >= upto) {text = text.slice(upto - pos); pos = upto; break} pos = end; spanStartStyle = ""; } text = allText.slice(at, at = styles[i++]); style = interpretTokenStyle(styles[i++], builder.cm.options); } } } // These objects are used to represent the visible (currently drawn) // part of the document. A LineView may correspond to multiple // logical lines, if those are connected by collapsed ranges. function LineView(doc, line, lineN) { // The starting line this.line = line; // Continuing lines, if any this.rest = visualLineContinued(line); // Number of logical lines in this visual line this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1; this.node = this.text = null; this.hidden = lineIsHidden(doc, line); } // Create a range of LineView objects for the given lines. function buildViewArray(cm, from, to) { var array = [], nextPos; for (var pos = from; pos < to; pos = nextPos) { var view = new LineView(cm.doc, getLine(cm.doc, pos), pos); nextPos = pos + view.size; array.push(view); } return array } var operationGroup = null; function pushOperation(op) { if (operationGroup) { operationGroup.ops.push(op); } else { op.ownsGroup = operationGroup = { ops: [op], delayedCallbacks: [] }; } } function fireCallbacksForOps(group) { // Calls delayed callbacks and cursorActivity handlers until no // new ones appear var callbacks = group.delayedCallbacks, i = 0; do { for (; i < callbacks.length; i++) { callbacks[i].call(null); } for (var j = 0; j < group.ops.length; j++) { var op = group.ops[j]; if (op.cursorActivityHandlers) { while (op.cursorActivityCalled < op.cursorActivityHandlers.length) { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } } } } while (i < callbacks.length) } function finishOperation(op, endCb) { var group = op.ownsGroup; if (!group) { return } try { fireCallbacksForOps(group); } finally { operationGroup = null; endCb(group); } } var orphanDelayedCallbacks = null; // Often, we want to signal events at a point where we are in the // middle of some work, but don't want the handler to start calling // other methods on the editor, which might be in an inconsistent // state or simply not expect any other events to happen. // signalLater looks whether there are any handlers, and schedules // them to be executed when the last operation ends, or, if no // operation is active, when a timeout fires. function signalLater(emitter, type /*, values...*/) { var arr = getHandlers(emitter, type); if (!arr.length) { return } var args = Array.prototype.slice.call(arguments, 2), list; if (operationGroup) { list = operationGroup.delayedCallbacks; } else if (orphanDelayedCallbacks) { list = orphanDelayedCallbacks; } else { list = orphanDelayedCallbacks = []; setTimeout(fireOrphanDelayed, 0); } var loop = function ( i ) { list.push(function () { return arr[i].apply(null, args); }); }; for (var i = 0; i < arr.length; ++i) loop( i ); } function fireOrphanDelayed() { var delayed = orphanDelayedCallbacks; orphanDelayedCallbacks = null; for (var i = 0; i < delayed.length; ++i) { delayed[i](); } } // When an aspect of a line changes, a string is added to // lineView.changes. This updates the relevant part of the line's // DOM structure. function updateLineForChanges(cm, lineView, lineN, dims) { for (var j = 0; j < lineView.changes.length; j++) { var type = lineView.changes[j]; if (type == "text") { updateLineText(cm, lineView); } else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); } else if (type == "class") { updateLineClasses(cm, lineView); } else if (type == "widget") { updateLineWidgets(cm, lineView, dims); } } lineView.changes = null; } // Lines with gutter elements, widgets or a background class need to // be wrapped, and have the extra elements added to the wrapper div function ensureLineWrapped(lineView) { if (lineView.node == lineView.text) { lineView.node = elt("div", null, null, "position: relative"); if (lineView.text.parentNode) { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); } lineView.node.appendChild(lineView.text); if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; } } return lineView.node } function updateLineBackground(cm, lineView) { var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass; if (cls) { cls += " CodeMirror-linebackground"; } if (lineView.background) { if (cls) { lineView.background.className = cls; } else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; } } else if (cls) { var wrap = ensureLineWrapped(lineView); lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild); cm.display.input.setUneditable(lineView.background); } } // Wrapper around buildLineContent which will reuse the structure // in display.externalMeasured when possible. function getLineContent(cm, lineView) { var ext = cm.display.externalMeasured; if (ext && ext.line == lineView.line) { cm.display.externalMeasured = null; lineView.measure = ext.measure; return ext.built } return buildLineContent(cm, lineView) } // Redraw the line's text. Interacts with the background and text // classes because the mode may output tokens that influence these // classes. function updateLineText(cm, lineView) { var cls = lineView.text.className; var built = getLineContent(cm, lineView); if (lineView.text == lineView.node) { lineView.node = built.pre; } lineView.text.parentNode.replaceChild(built.pre, lineView.text); lineView.text = built.pre; if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) { lineView.bgClass = built.bgClass; lineView.textClass = built.textClass; updateLineClasses(cm, lineView); } else if (cls) { lineView.text.className = cls; } } function updateLineClasses(cm, lineView) { updateLineBackground(cm, lineView); if (lineView.line.wrapClass) { ensureLineWrapped(lineView).className = lineView.line.wrapClass; } else if (lineView.node != lineView.text) { lineView.node.className = ""; } var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass; lineView.text.className = textClass || ""; } function updateLineGutter(cm, lineView, lineN, dims) { if (lineView.gutter) { lineView.node.removeChild(lineView.gutter); lineView.gutter = null; } if (lineView.gutterBackground) { lineView.node.removeChild(lineView.gutterBackground); lineView.gutterBackground = null; } if (lineView.line.gutterClass) { var wrap = ensureLineWrapped(lineView); lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass, ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px")); cm.display.input.setUneditable(lineView.gutterBackground); wrap.insertBefore(lineView.gutterBackground, lineView.text); } var markers = lineView.line.gutterMarkers; if (cm.options.lineNumbers || markers) { var wrap$1 = ensureLineWrapped(lineView); var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px")); cm.display.input.setUneditable(gutterWrap); wrap$1.insertBefore(gutterWrap, lineView.text); if (lineView.line.gutterClass) { gutterWrap.className += " " + lineView.line.gutterClass; } if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) { lineView.lineNumber = gutterWrap.appendChild( elt("div", lineNumberFor(cm.options, lineN), "CodeMirror-linenumber CodeMirror-gutter-elt", ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); } if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) { var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id]; if (found) { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); } } } } } function updateLineWidgets(cm, lineView, dims) { if (lineView.alignable) { lineView.alignable = null; } var isWidget = classTest("CodeMirror-linewidget"); for (var node = lineView.node.firstChild, next = (void 0); node; node = next) { next = node.nextSibling; if (isWidget.test(node.className)) { lineView.node.removeChild(node); } } insertLineWidgets(cm, lineView, dims); } // Build a line's DOM representation from scratch function buildLineElement(cm, lineView, lineN, dims) { var built = getLineContent(cm, lineView); lineView.text = lineView.node = built.pre; if (built.bgClass) { lineView.bgClass = built.bgClass; } if (built.textClass) { lineView.textClass = built.textClass; } updateLineClasses(cm, lineView); updateLineGutter(cm, lineView, lineN, dims); insertLineWidgets(cm, lineView, dims); return lineView.node } // A lineView may contain multiple logical lines (when merged by // collapsed spans). The widgets for all of them need to be drawn. function insertLineWidgets(cm, lineView, dims) { insertLineWidgetsFor(cm, lineView.line, lineView, dims, true); if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } } } function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { if (!line.widgets) { return } var wrap = ensureLineWrapped(lineView); for (var i = 0, ws = line.widgets; i < ws.length; ++i) { var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : "")); if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); } positionLineWidget(widget, node, lineView, dims); cm.display.input.setUneditable(node); if (allowAbove && widget.above) { wrap.insertBefore(node, lineView.gutter || lineView.text); } else { wrap.appendChild(node); } signalLater(widget, "redraw"); } } function positionLineWidget(widget, node, lineView, dims) { if (widget.noHScroll) { (lineView.alignable || (lineView.alignable = [])).push(node); var width = dims.wrapperWidth; node.style.left = dims.fixedPos + "px"; if (!widget.coverGutter) { width -= dims.gutterTotalWidth; node.style.paddingLeft = dims.gutterTotalWidth + "px"; } node.style.width = width + "px"; } if (widget.coverGutter) { node.style.zIndex = 5; node.style.position = "relative"; if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; } } } function widgetHeight(widget) { if (widget.height != null) { return widget.height } var cm = widget.doc.cm; if (!cm) { return 0 } if (!contains(document.body, widget.node)) { var parentStyle = "position: relative;"; if (widget.coverGutter) { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; } if (widget.noHScroll) { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; } removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle)); } return widget.height = widget.node.parentNode.offsetHeight } // Return true when the given mouse event happened in a widget function eventInWidget(display, e) { for (var n = e_target(e); n != display.wrapper; n = n.parentNode) { if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") || (n.parentNode == display.sizer && n != display.mover)) { return true } } } // POSITION MEASUREMENT function paddingTop(display) {return display.lineSpace.offsetTop} function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight} function paddingH(display) { if (display.cachedPaddingH) { return display.cachedPaddingH } var e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like")); var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle; var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}; if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; } return data } function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth } function displayWidth(cm) { return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth } function displayHeight(cm) { return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight } // Ensure the lineView.wrapping.heights array is populated. This is // an array of bottom offsets for the lines that make up a drawn // line. When lineWrapping is on, there might be more than one // height. function ensureLineHeights(cm, lineView, rect) { var wrapping = cm.options.lineWrapping; var curWidth = wrapping && displayWidth(cm); if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) { var heights = lineView.measure.heights = []; if (wrapping) { lineView.measure.width = curWidth; var rects = lineView.text.firstChild.getClientRects(); for (var i = 0; i < rects.length - 1; i++) { var cur = rects[i], next = rects[i + 1]; if (Math.abs(cur.bottom - next.bottom) > 2) { heights.push((cur.bottom + next.top) / 2 - rect.top); } } } heights.push(rect.bottom - rect.top); } } // Find a line map (mapping character offsets to text nodes) and a // measurement cache for the given line number. (A line view might // contain multiple lines when collapsed ranges are present.) function mapFromLineView(lineView, line, lineN) { if (lineView.line == line) { return {map: lineView.measure.map, cache: lineView.measure.cache} } for (var i = 0; i < lineView.rest.length; i++) { if (lineView.rest[i] == line) { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } } for (var i$1 = 0; i$1 < lineView.rest.length; i$1++) { if (lineNo(lineView.rest[i$1]) > lineN) { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } } } // Render a line into the hidden node display.externalMeasured. Used // when measurement is needed for a line that's not in the viewport. function updateExternalMeasurement(cm, line) { line = visualLine(line); var lineN = lineNo(line); var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN); view.lineN = lineN; var built = view.built = buildLineContent(cm, view); view.text = built.pre; removeChildrenAndAdd(cm.display.lineMeasure, built.pre); return view } // Get a {top, bottom, left, right} box (in line-local coordinates) // for a given character. function measureChar(cm, line, ch, bias) { return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias) } // Find a line view that corresponds to the given line number. function findViewForLine(cm, lineN) { if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) { return cm.display.view[findViewIndex(cm, lineN)] } var ext = cm.display.externalMeasured; if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size) { return ext } } // Measurement can be split in two steps, the set-up work that // applies to the whole line, and the measurement of the actual // character. Functions like coordsChar, that need to do a lot of // measurements in a row, can thus ensure that the set-up work is // only done once. function prepareMeasureForLine(cm, line) { var lineN = lineNo(line); var view = findViewForLine(cm, lineN); if (view && !view.text) { view = null; } else if (view && view.changes) { updateLineForChanges(cm, view, lineN, getDimensions(cm)); cm.curOp.forceUpdate = true; } if (!view) { view = updateExternalMeasurement(cm, line); } var info = mapFromLineView(view, line, lineN); return { line: line, view: view, rect: null, map: info.map, cache: info.cache, before: info.before, hasHeights: false } } // Given a prepared measurement object, measures the position of an // actual character (or fetches it from the cache). function measureCharPrepared(cm, prepared, ch, bias, varHeight) { if (prepared.before) { ch = -1; } var key = ch + (bias || ""), found; if (prepared.cache.hasOwnProperty(key)) { found = prepared.cache[key]; } else { if (!prepared.rect) { prepared.rect = prepared.view.text.getBoundingClientRect(); } if (!prepared.hasHeights) { ensureLineHeights(cm, prepared.view, prepared.rect); prepared.hasHeights = true; } found = measureCharInner(cm, prepared, ch, bias); if (!found.bogus) { prepared.cache[key] = found; } } return {left: found.left, right: found.right, top: varHeight ? found.rtop : found.top, bottom: varHeight ? found.rbottom : found.bottom} } var nullRect = {left: 0, right: 0, top: 0, bottom: 0}; function nodeAndOffsetInLineMap(map, ch, bias) { var node, start, end, collapse, mStart, mEnd; // First, search the line map for the text node corresponding to, // or closest to, the target character. for (var i = 0; i < map.length; i += 3) { mStart = map[i]; mEnd = map[i + 1]; if (ch < mStart) { start = 0; end = 1; collapse = "left"; } else if (ch < mEnd) { start = ch - mStart; end = start + 1; } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) { end = mEnd - mStart; start = end - 1; if (ch >= mEnd) { collapse = "right"; } } if (start != null) { node = map[i + 2]; if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) { collapse = bias; } if (bias == "left" && start == 0) { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) { node = map[(i -= 3) + 2]; collapse = "left"; } } if (bias == "right" && start == mEnd - mStart) { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) { node = map[(i += 3) + 2]; collapse = "right"; } } break } } return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd} } function getUsefulRect(rects, bias) { var rect = nullRect; if (bias == "left") { for (var i = 0; i < rects.length; i++) { if ((rect = rects[i]).left != rect.right) { break } } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) { if ((rect = rects[i$1]).left != rect.right) { break } } } return rect } function measureCharInner(cm, prepared, ch, bias) { var place = nodeAndOffsetInLineMap(prepared.map, ch, bias); var node = place.node, start = place.start, end = place.end, collapse = place.collapse; var rect; if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates. for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; } while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; } if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) { rect = node.parentNode.getBoundingClientRect(); } else { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); } if (rect.left || rect.right || start == 0) { break } end = start; start = start - 1; collapse = "right"; } if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); } } else { // If it is a widget, simply get the box for the whole widget. if (start > 0) { collapse = bias = "right"; } var rects; if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) { rect = rects[bias == "right" ? rects.length - 1 : 0]; } else { rect = node.getBoundingClientRect(); } } if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) { var rSpan = node.parentNode.getClientRects()[0]; if (rSpan) { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; } else { rect = nullRect; } } var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top; var mid = (rtop + rbot) / 2; var heights = prepared.view.measure.heights; var i = 0; for (; i < heights.length - 1; i++) { if (mid < heights[i]) { break } } var top = i ? heights[i - 1] : 0, bot = heights[i]; var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left, right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left, top: top, bottom: bot}; if (!rect.left && !rect.right) { result.bogus = true; } if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; } return result } // Work around problem with bounding client rects on ranges being // returned incorrectly when zoomed on IE10 and below. function maybeUpdateRectForZooming(measure, rect) { if (!window.screen || screen.logicalXDPI == null || screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure)) { return rect } var scaleX = screen.logicalXDPI / screen.deviceXDPI; var scaleY = screen.logicalYDPI / screen.deviceYDPI; return {left: rect.left * scaleX, right: rect.right * scaleX, top: rect.top * scaleY, bottom: rect.bottom * scaleY} } function clearLineMeasurementCacheFor(lineView) { if (lineView.measure) { lineView.measure.cache = {}; lineView.measure.heights = null; if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) { lineView.measure.caches[i] = {}; } } } } function clearLineMeasurementCache(cm) { cm.display.externalMeasure = null; removeChildren(cm.display.lineMeasure); for (var i = 0; i < cm.display.view.length; i++) { clearLineMeasurementCacheFor(cm.display.view[i]); } } function clearCaches(cm) { clearLineMeasurementCache(cm); cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null; if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; } cm.display.lineNumChars = null; } function pageScrollX() { // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206 // which causes page_Offset and bounding client rects to use // different reference viewports and invalidate our calculations. if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) } return window.pageXOffset || (document.documentElement || document.body).scrollLeft } function pageScrollY() { if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) } return window.pageYOffset || (document.documentElement || document.body).scrollTop } function widgetTopHeight(lineObj) { var height = 0; if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above) { height += widgetHeight(lineObj.widgets[i]); } } } return height } // Converts a {top, bottom, left, right} box from line-local // coordinates into another coordinate system. Context may be one of // "line", "div" (display.lineDiv), "local"./null (editor), "window", // or "page". function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) { if (!includeWidgets) { var height = widgetTopHeight(lineObj); rect.top += height; rect.bottom += height; } if (context == "line") { return rect } if (!context) { context = "local"; } var yOff = heightAtLine(lineObj); if (context == "local") { yOff += paddingTop(cm.display); } else { yOff -= cm.display.viewOffset; } if (context == "page" || context == "window") { var lOff = cm.display.lineSpace.getBoundingClientRect(); yOff += lOff.top + (context == "window" ? 0 : pageScrollY()); var xOff = lOff.left + (context == "window" ? 0 : pageScrollX()); rect.left += xOff; rect.right += xOff; } rect.top += yOff; rect.bottom += yOff; return rect } // Coverts a box from "div" coords to another coordinate system. // Context may be "window", "page", "div", or "local"./null. function fromCoordSystem(cm, coords, context) { if (context == "div") { return coords } var left = coords.left, top = coords.top; // First move into "page" coordinate system if (context == "page") { left -= pageScrollX(); top -= pageScrollY(); } else if (context == "local" || !context) { var localBox = cm.display.sizer.getBoundingClientRect(); left += localBox.left; top += localBox.top; } var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect(); return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top} } function charCoords(cm, pos, context, lineObj, bias) { if (!lineObj) { lineObj = getLine(cm.doc, pos.line); } return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context) } // Returns a box for a given cursor position, which may have an // 'other' property containing the position of the secondary cursor // on a bidi boundary. // A cursor Pos(line, char, "before") is on the same visual line as `char - 1` // and after `char - 1` in writing order of `char - 1` // A cursor Pos(line, char, "after") is on the same visual line as `char` // and before `char` in writing order of `char` // Examples (upper-case letters are RTL, lower-case are LTR): // Pos(0, 1, ...) // before after // ab a|b a|b // aB a|B aB| // Ab |Ab A|b // AB B|A B|A // Every position after the last character on a line is considered to stick // to the last character on the line. function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) { lineObj = lineObj || getLine(cm.doc, pos.line); if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } function get(ch, right) { var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight); if (right) { m.left = m.right; } else { m.right = m.left; } return intoCoordSystem(cm, lineObj, m, context) } var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky; if (ch >= lineObj.text.length) { ch = lineObj.text.length; sticky = "before"; } else if (ch <= 0) { ch = 0; sticky = "after"; } if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") } function getBidi(ch, partPos, invert) { var part = order[partPos], right = part.level == 1; return get(invert ? ch - 1 : ch, right != invert) } var partPos = getBidiPartAt(order, ch, sticky); var other = bidiOther; var val = getBidi(ch, partPos, sticky == "before"); if (other != null) { val.other = getBidi(ch, other, sticky != "before"); } return val } // Used to cheaply estimate the coordinates for a position. Used for // intermediate scroll updates. function estimateCoords(cm, pos) { var left = 0; pos = clipPos(cm.doc, pos); if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; } var lineObj = getLine(cm.doc, pos.line); var top = heightAtLine(lineObj) + paddingTop(cm.display); return {left: left, right: left, top: top, bottom: top + lineObj.height} } // Positions returned by coordsChar contain some extra information. // xRel is the relative x position of the input coordinates compared // to the found position (so xRel > 0 means the coordinates are to // the right of the character position, for example). When outside // is true, that means the coordinates lie outside the line's // vertical range. function PosWithInfo(line, ch, sticky, outside, xRel) { var pos = Pos(line, ch, sticky); pos.xRel = xRel; if (outside) { pos.outside = outside; } return pos } // Compute the character position closest to the given coordinates. // Input must be lineSpace-local ("div" coordinate system). function coordsChar(cm, x, y) { var doc = cm.doc; y += cm.display.viewOffset; if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) } var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1; if (lineN > last) { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) } if (x < 0) { x = 0; } var lineObj = getLine(doc, lineN); for (;;) { var found = coordsCharInner(cm, lineObj, lineN, x, y); var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0)); if (!collapsed) { return found } var rangeEnd = collapsed.find(1); if (rangeEnd.line == lineN) { return rangeEnd } lineObj = getLine(doc, lineN = rangeEnd.line); } } function wrappedLineExtent(cm, lineObj, preparedMeasure, y) { y -= widgetTopHeight(lineObj); var end = lineObj.text.length; var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0); end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end); return {begin: begin, end: end} } function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) { if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top; return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop) } // Returns true if the given side of a box is after the given // coordinates, in top-to-bottom, left-to-right order. function boxIsAfter(box, x, y, left) { return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x } function coordsCharInner(cm, lineObj, lineNo, x, y) { // Move y into line-local coordinate space y -= heightAtLine(lineObj); var preparedMeasure = prepareMeasureForLine(cm, lineObj); // When directly calling `measureCharPrepared`, we have to adjust // for the widgets at this line. var widgetHeight = widgetTopHeight(lineObj); var begin = 0, end = lineObj.text.length, ltr = true; var order = getOrder(lineObj, cm.doc.direction); // If the line isn't plain left-to-right text, first figure out // which bidi section the coordinates fall into. if (order) { var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart) (cm, lineObj, lineNo, preparedMeasure, order, x, y); ltr = part.level != 1; // The awkward -1 offsets are needed because findFirst (called // on these below) will treat its first bound as inclusive, // second as exclusive, but we want to actually address the // characters in the part's range begin = ltr ? part.from : part.to - 1; end = ltr ? part.to : part.from - 1; } // A binary search to find the first character whose bounding box // starts after the coordinates. If we run across any whose box wrap // the coordinates, store that. var chAround = null, boxAround = null; var ch = findFirst(function (ch) { var box = measureCharPrepared(cm, preparedMeasure, ch); box.top += widgetHeight; box.bottom += widgetHeight; if (!boxIsAfter(box, x, y, false)) { return false } if (box.top <= y && box.left <= x) { chAround = ch; boxAround = box; } return true }, begin, end); var baseX, sticky, outside = false; // If a box around the coordinates was found, use that if (boxAround) { // Distinguish coordinates nearer to the left or right side of the box var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr; ch = chAround + (atStart ? 0 : 1); sticky = atStart ? "after" : "before"; baseX = atLeft ? boxAround.left : boxAround.right; } else { // (Adjust for extended bound, if necessary.) if (!ltr && (ch == end || ch == begin)) { ch++; } // To determine which side to associate with, get the box to the // left of the character and compare it's vertical position to the // coordinates sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" : (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ? "after" : "before"; // Now get accurate coordinates for this place, in order to get a // base X position var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure); baseX = coords.left; outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0; } ch = skipExtendingChars(lineObj.text, ch, 1); return PosWithInfo(lineNo, ch, sticky, outside, x - baseX) } function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) { // Bidi parts are sorted left-to-right, and in a non-line-wrapping // situation, we can take this ordering to correspond to the visual // ordering. This finds the first part whose end is after the given // coordinates. var index = findFirst(function (i) { var part = order[i], ltr = part.level != 1; return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"), "line", lineObj, preparedMeasure), x, y, true) }, 0, order.length - 1); var part = order[index]; // If this isn't the first part, the part's start is also after // the coordinates, and the coordinates aren't on the same line as // that start, move one part back. if (index > 0) { var ltr = part.level != 1; var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"), "line", lineObj, preparedMeasure); if (boxIsAfter(start, x, y, true) && start.top > y) { part = order[index - 1]; } } return part } function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) { // In a wrapped line, rtl text on wrapping boundaries can do things // that don't correspond to the ordering in our `order` array at // all, so a binary search doesn't work, and we want to return a // part that only spans one line so that the binary search in // coordsCharInner is safe. As such, we first find the extent of the // wrapped line, and then do a flat search in which we discard any // spans that aren't on the line. var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y); var begin = ref.begin; var end = ref.end; if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; } var part = null, closestDist = null; for (var i = 0; i < order.length; i++) { var p = order[i]; if (p.from >= end || p.to <= begin) { continue } var ltr = p.level != 1; var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right; // Weigh against spans ending before this, so that they are only // picked if nothing ends after var dist = endX < x ? x - endX + 1e9 : endX - x; if (!part || closestDist > dist) { part = p; closestDist = dist; } } if (!part) { part = order[order.length - 1]; } // Clip the part to the wrapped line. if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; } if (part.to > end) { part = {from: part.from, to: end, level: part.level}; } return part } var measureText; // Compute the default text height. function textHeight(display) { if (display.cachedTextHeight != null) { return display.cachedTextHeight } if (measureText == null) { measureText = elt("pre", null, "CodeMirror-line-like"); // Measure a bunch of lines, for browsers that compute // fractional heights. for (var i = 0; i < 49; ++i) { measureText.appendChild(document.createTextNode("x")); measureText.appendChild(elt("br")); } measureText.appendChild(document.createTextNode("x")); } removeChildrenAndAdd(display.measure, measureText); var height = measureText.offsetHeight / 50; if (height > 3) { display.cachedTextHeight = height; } removeChildren(display.measure); return height || 1 } // Compute the default character width. function charWidth(display) { if (display.cachedCharWidth != null) { return display.cachedCharWidth } var anchor = elt("span", "xxxxxxxxxx"); var pre = elt("pre", [anchor], "CodeMirror-line-like"); removeChildrenAndAdd(display.measure, pre); var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10; if (width > 2) { display.cachedCharWidth = width; } return width || 10 } // Do a bulk-read of the DOM positions and sizes needed to draw the // view, so that we don't interleave reading and writing to the DOM. function getDimensions(cm) { var d = cm.display, left = {}, width = {}; var gutterLeft = d.gutters.clientLeft; for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { var id = cm.display.gutterSpecs[i].className; left[id] = n.offsetLeft + n.clientLeft + gutterLeft; width[id] = n.clientWidth; } return {fixedPos: compensateForHScroll(d), gutterTotalWidth: d.gutters.offsetWidth, gutterLeft: left, gutterWidth: width, wrapperWidth: d.wrapper.clientWidth} } // Computes display.scroller.scrollLeft + display.gutters.offsetWidth, // but using getBoundingClientRect to get a sub-pixel-accurate // result. function compensateForHScroll(display) { return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left } // Returns a function that estimates the height of a line, to use as // first approximation until the line becomes visible (and is thus // properly measurable). function estimateHeight(cm) { var th = textHeight(cm.display), wrapping = cm.options.lineWrapping; var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3); return function (line) { if (lineIsHidden(cm.doc, line)) { return 0 } var widgetsHeight = 0; if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) { if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; } } } if (wrapping) { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th } else { return widgetsHeight + th } } } function estimateLineHeights(cm) { var doc = cm.doc, est = estimateHeight(cm); doc.iter(function (line) { var estHeight = est(line); if (estHeight != line.height) { updateLineHeight(line, estHeight); } }); } // Given a mouse event, find the corresponding position. If liberal // is false, it checks whether a gutter or scrollbar was clicked, // and returns null if it was. forRect is used by rectangular // selections, and tries to estimate a character position even for // coordinates beyond the right of the text. function posFromMouse(cm, e, liberal, forRect) { var display = cm.display; if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null } var x, y, space = display.lineSpace.getBoundingClientRect(); // Fails unpredictably on IE[67] when mouse is dragged around quickly. try { x = e.clientX - space.left; y = e.clientY - space.top; } catch (e) { return null } var coords = coordsChar(cm, x, y), line; if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length; coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff)); } return coords } // Find the view element corresponding to a given line. Return null // when the line isn't visible. function findViewIndex(cm, n) { if (n >= cm.display.viewTo) { return null } n -= cm.display.viewFrom; if (n < 0) { return null } var view = cm.display.view; for (var i = 0; i < view.length; i++) { n -= view[i].size; if (n < 0) { return i } } } // Updates the display.view data structure for a given change to the // document. From and to are in pre-change coordinates. Lendiff is // the amount of lines added or subtracted by the change. This is // used for changes that span multiple lines, or change the way // lines are divided into visual lines. regLineChange (below) // registers single-line changes. function regChange(cm, from, to, lendiff) { if (from == null) { from = cm.doc.first; } if (to == null) { to = cm.doc.first + cm.doc.size; } if (!lendiff) { lendiff = 0; } var display = cm.display; if (lendiff && to < display.viewTo && (display.updateLineNumbers == null || display.updateLineNumbers > from)) { display.updateLineNumbers = from; } cm.curOp.viewChanged = true; if (from >= display.viewTo) { // Change after if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo) { resetView(cm); } } else if (to <= display.viewFrom) { // Change before if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) { resetView(cm); } else { display.viewFrom += lendiff; display.viewTo += lendiff; } } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap resetView(cm); } else if (from <= display.viewFrom) { // Top overlap var cut = viewCuttingPoint(cm, to, to + lendiff, 1); if (cut) { display.view = display.view.slice(cut.index); display.viewFrom = cut.lineN; display.viewTo += lendiff; } else { resetView(cm); } } else if (to >= display.viewTo) { // Bottom overlap var cut$1 = viewCuttingPoint(cm, from, from, -1); if (cut$1) { display.view = display.view.slice(0, cut$1.index); display.viewTo = cut$1.lineN; } else { resetView(cm); } } else { // Gap in the middle var cutTop = viewCuttingPoint(cm, from, from, -1); var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1); if (cutTop && cutBot) { display.view = display.view.slice(0, cutTop.index) .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN)) .concat(display.view.slice(cutBot.index)); display.viewTo += lendiff; } else { resetView(cm); } } var ext = display.externalMeasured; if (ext) { if (to < ext.lineN) { ext.lineN += lendiff; } else if (from < ext.lineN + ext.size) { display.externalMeasured = null; } } } // Register a change to a single line. Type must be one of "text", // "gutter", "class", "widget" function regLineChange(cm, line, type) { cm.curOp.viewChanged = true; var display = cm.display, ext = cm.display.externalMeasured; if (ext && line >= ext.lineN && line < ext.lineN + ext.size) { display.externalMeasured = null; } if (line < display.viewFrom || line >= display.viewTo) { return } var lineView = display.view[findViewIndex(cm, line)]; if (lineView.node == null) { return } var arr = lineView.changes || (lineView.changes = []); if (indexOf(arr, type) == -1) { arr.push(type); } } // Clear the view. function resetView(cm) { cm.display.viewFrom = cm.display.viewTo = cm.doc.first; cm.display.view = []; cm.display.viewOffset = 0; } function viewCuttingPoint(cm, oldN, newN, dir) { var index = findViewIndex(cm, oldN), diff, view = cm.display.view; if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) { return {index: index, lineN: newN} } var n = cm.display.viewFrom; for (var i = 0; i < index; i++) { n += view[i].size; } if (n != oldN) { if (dir > 0) { if (index == view.length - 1) { return null } diff = (n + view[index].size) - oldN; index++; } else { diff = n - oldN; } oldN += diff; newN += diff; } while (visualLineNo(cm.doc, newN) != newN) { if (index == (dir < 0 ? 0 : view.length - 1)) { return null } newN += dir * view[index - (dir < 0 ? 1 : 0)].size; index += dir; } return {index: index, lineN: newN} } // Force the view to cover a given range, adding empty view element // or clipping off existing ones as needed. function adjustView(cm, from, to) { var display = cm.display, view = display.view; if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) { display.view = buildViewArray(cm, from, to); display.viewFrom = from; } else { if (display.viewFrom > from) { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); } else if (display.viewFrom < from) { display.view = display.view.slice(findViewIndex(cm, from)); } display.viewFrom = from; if (display.viewTo < to) { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); } else if (display.viewTo > to) { display.view = display.view.slice(0, findViewIndex(cm, to)); } } display.viewTo = to; } // Count the number of lines in the view whose DOM representation is // out of date (or nonexistent). function countDirtyView(cm) { var view = cm.display.view, dirty = 0; for (var i = 0; i < view.length; i++) { var lineView = view[i]; if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; } } return dirty } function updateSelection(cm) { cm.display.input.showSelection(cm.display.input.prepareSelection()); } function prepareSelection(cm, primary) { if ( primary === void 0 ) primary = true; var doc = cm.doc, result = {}; var curFragment = result.cursors = document.createDocumentFragment(); var selFragment = result.selection = document.createDocumentFragment(); for (var i = 0; i < doc.sel.ranges.length; i++) { if (!primary && i == doc.sel.primIndex) { continue } var range = doc.sel.ranges[i]; if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue } var collapsed = range.empty(); if (collapsed || cm.options.showCursorWhenSelecting) { drawSelectionCursor(cm, range.head, curFragment); } if (!collapsed) { drawSelectionRange(cm, range, selFragment); } } return result } // Draws a cursor for the given range function drawSelectionCursor(cm, head, output) { var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine); var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); cursor.style.left = pos.left + "px"; cursor.style.top = pos.top + "px"; cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; if (pos.other) { // Secondary cursor, shown when on a 'jump' in bi-directional text var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")); otherCursor.style.display = ""; otherCursor.style.left = pos.other.left + "px"; otherCursor.style.top = pos.other.top + "px"; otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"; } } function cmpCoords(a, b) { return a.top - b.top || a.left - b.left } // Draws the given range as a highlighted selection function drawSelectionRange(cm, range, output) { var display = cm.display, doc = cm.doc; var fragment = document.createDocumentFragment(); var padding = paddingH(cm.display), leftSide = padding.left; var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right; var docLTR = doc.direction == "ltr"; function add(left, top, width, bottom) { if (top < 0) { top = 0; } top = Math.round(top); bottom = Math.round(bottom); fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n height: " + (bottom - top) + "px"))); } function drawForLine(line, fromArg, toArg) { var lineObj = getLine(doc, line); var lineLen = lineObj.text.length; var start, end; function coords(ch, bias) { return charCoords(cm, Pos(line, ch), "div", lineObj, bias) } function wrapX(pos, dir, side) { var extent = wrappedLineExtentChar(cm, lineObj, null, pos); var prop = (dir == "ltr") == (side == "after") ? "left" : "right"; var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1); return coords(ch, prop)[prop] } var order = getOrder(lineObj, doc.direction); iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) { var ltr = dir == "ltr"; var fromPos = coords(from, ltr ? "left" : "right"); var toPos = coords(to - 1, ltr ? "right" : "left"); var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen; var first = i == 0, last = !order || i == order.length - 1; if (toPos.top - fromPos.top <= 3) { // Single line var openLeft = (docLTR ? openStart : openEnd) && first; var openRight = (docLTR ? openEnd : openStart) && last; var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left; var right = openRight ? rightSide : (ltr ? toPos : fromPos).right; add(left, fromPos.top, right - left, fromPos.bottom); } else { // Multiple lines var topLeft, topRight, botLeft, botRight; if (ltr) { topLeft = docLTR && openStart && first ? leftSide : fromPos.left; topRight = docLTR ? rightSide : wrapX(from, dir, "before"); botLeft = docLTR ? leftSide : wrapX(to, dir, "after"); botRight = docLTR && openEnd && last ? rightSide : toPos.right; } else { topLeft = !docLTR ? leftSide : wrapX(from, dir, "before"); topRight = !docLTR && openStart && first ? rightSide : fromPos.right; botLeft = !docLTR && openEnd && last ? leftSide : toPos.left; botRight = !docLTR ? rightSide : wrapX(to, dir, "after"); } add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom); if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); } add(botLeft, toPos.top, botRight - botLeft, toPos.bottom); } if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; } if (cmpCoords(toPos, start) < 0) { start = toPos; } if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; } if (cmpCoords(toPos, end) < 0) { end = toPos; } }); return {start: start, end: end} } var sFrom = range.from(), sTo = range.to(); if (sFrom.line == sTo.line) { drawForLine(sFrom.line, sFrom.ch, sTo.ch); } else { var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line); var singleVLine = visualLine(fromLine) == visualLine(toLine); var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end; var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start; if (singleVLine) { if (leftEnd.top < rightStart.top - 2) { add(leftEnd.right, leftEnd.top, null, leftEnd.bottom); add(leftSide, rightStart.top, rightStart.left, rightStart.bottom); } else { add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom); } } if (leftEnd.bottom < rightStart.top) { add(leftSide, leftEnd.bottom, null, rightStart.top); } } output.appendChild(fragment); } // Cursor-blinking function restartBlink(cm) { if (!cm.state.focused) { return } var display = cm.display; clearInterval(display.blinker); var on = true; display.cursorDiv.style.visibility = ""; if (cm.options.cursorBlinkRate > 0) { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; }, cm.options.cursorBlinkRate); } else if (cm.options.cursorBlinkRate < 0) { display.cursorDiv.style.visibility = "hidden"; } } function ensureFocus(cm) { if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); } } function delayBlurEvent(cm) { cm.state.delayingBlurEvent = true; setTimeout(function () { if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; onBlur(cm); } }, 100); } function onFocus(cm, e) { if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; } if (cm.options.readOnly == "nocursor") { return } if (!cm.state.focused) { signal(cm, "focus", cm, e); cm.state.focused = true; addClass(cm.display.wrapper, "CodeMirror-focused"); // This test prevents this from firing when a context // menu is closed (since the input reset would kill the // select-all detection hack) if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { cm.display.input.reset(); if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730 } cm.display.input.receivedFocus(); } restartBlink(cm); } function onBlur(cm, e) { if (cm.state.delayingBlurEvent) { return } if (cm.state.focused) { signal(cm, "blur", cm, e); cm.state.focused = false; rmClass(cm.display.wrapper, "CodeMirror-focused"); } clearInterval(cm.display.blinker); setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150); } // Read the actual heights of the rendered lines, and update their // stored heights to match. function updateHeightsInViewport(cm) { var display = cm.display; var prevBottom = display.lineDiv.offsetTop; for (var i = 0; i < display.view.length; i++) { var cur = display.view[i], wrapping = cm.options.lineWrapping; var height = (void 0), width = 0; if (cur.hidden) { continue } if (ie && ie_version < 8) { var bot = cur.node.offsetTop + cur.node.offsetHeight; height = bot - prevBottom; prevBottom = bot; } else { var box = cur.node.getBoundingClientRect(); height = box.bottom - box.top; // Check that lines don't extend past the right of the current // editor width if (!wrapping && cur.text.firstChild) { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; } } var diff = cur.line.height - height; if (diff > .005 || diff < -.005) { updateLineHeight(cur.line, height); updateWidgetHeight(cur.line); if (cur.rest) { for (var j = 0; j < cur.rest.length; j++) { updateWidgetHeight(cur.rest[j]); } } } if (width > cm.display.sizerWidth) { var chWidth = Math.ceil(width / charWidth(cm.display)); if (chWidth > cm.display.maxLineLength) { cm.display.maxLineLength = chWidth; cm.display.maxLine = cur.line; cm.display.maxLineChanged = true; } } } } // Read and store the height of line widgets associated with the // given line. function updateWidgetHeight(line) { if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) { var w = line.widgets[i], parent = w.node.parentNode; if (parent) { w.height = parent.offsetHeight; } } } } // Compute the lines that are visible in a given viewport (defaults // the the current scroll position). viewport may contain top, // height, and ensure (see op.scrollToPos) properties. function visibleLines(display, doc, viewport) { var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop; top = Math.floor(top - paddingTop(display)); var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight; var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom); // Ensure is a {from: {line, ch}, to: {line, ch}} object, and // forces those lines into the viewport (if possible). if (viewport && viewport.ensure) { var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line; if (ensureFrom < from) { from = ensureFrom; to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight); } else if (Math.min(ensureTo, doc.lastLine()) >= to) { from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight); to = ensureTo; } } return {from: from, to: Math.max(to, from + 1)} } // SCROLLING THINGS INTO VIEW // If an editor sits on the top or bottom of the window, partially // scrolled out of view, this ensures that the cursor is visible. function maybeScrollWindow(cm, rect) { if (signalDOMEvent(cm, "scrollCursorIntoView")) { return } var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; if (rect.top + box.top < 0) { doScroll = true; } else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; } if (doScroll != null && !phantom) { var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;")); cm.display.lineSpace.appendChild(scrollNode); scrollNode.scrollIntoView(doScroll); cm.display.lineSpace.removeChild(scrollNode); } } // Scroll a given position into view (immediately), verifying that // it actually became visible (as line heights are accurately // measured, the position of something may 'drift' during drawing). function scrollPosIntoView(cm, pos, end, margin) { if (margin == null) { margin = 0; } var rect; if (!cm.options.lineWrapping && pos == end) { // Set pos and end to the cursor positions around the character pos sticks to // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch // If pos == Pos(_, 0, "before"), pos and end are unchanged pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos; end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos; } for (var limit = 0; limit < 5; limit++) { var changed = false; var coords = cursorCoords(cm, pos); var endCoords = !end || end == pos ? coords : cursorCoords(cm, end); rect = {left: Math.min(coords.left, endCoords.left), top: Math.min(coords.top, endCoords.top) - margin, right: Math.max(coords.left, endCoords.left), bottom: Math.max(coords.bottom, endCoords.bottom) + margin}; var scrollPos = calculateScrollPos(cm, rect); var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft; if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; } } if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; } } if (!changed) { break } } return rect } // Scroll a given set of coordinates into view (immediately). function scrollIntoView(cm, rect) { var scrollPos = calculateScrollPos(cm, rect); if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); } if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); } } // Calculate a new scroll position needed to scroll the given // rectangle into view. Returns an object with scrollTop and // scrollLeft properties. When these are undefined, the // vertical/horizontal position does not need to be adjusted. function calculateScrollPos(cm, rect) { var display = cm.display, snapMargin = textHeight(cm.display); if (rect.top < 0) { rect.top = 0; } var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop; var screen = displayHeight(cm), result = {}; if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; } var docBottom = cm.doc.height + paddingVert(display); var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin; if (rect.top < screentop) { result.scrollTop = atTop ? 0 : rect.top; } else if (rect.bottom > screentop + screen) { var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen); if (newTop != screentop) { result.scrollTop = newTop; } } var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft; var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0); var tooWide = rect.right - rect.left > screenw; if (tooWide) { rect.right = rect.left + screenw; } if (rect.left < 10) { result.scrollLeft = 0; } else if (rect.left < screenleft) { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); } else if (rect.right > screenw + screenleft - 3) { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; } return result } // Store a relative adjustment to the scroll position in the current // operation (to be applied when the operation finishes). function addToScrollTop(cm, top) { if (top == null) { return } resolveScrollToPos(cm); cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top; } // Make sure that at the end of the operation the current cursor is // shown. function ensureCursorVisible(cm) { resolveScrollToPos(cm); var cur = cm.getCursor(); cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin}; } function scrollToCoords(cm, x, y) { if (x != null || y != null) { resolveScrollToPos(cm); } if (x != null) { cm.curOp.scrollLeft = x; } if (y != null) { cm.curOp.scrollTop = y; } } function scrollToRange(cm, range) { resolveScrollToPos(cm); cm.curOp.scrollToPos = range; } // When an operation has its scrollToPos property set, and another // scroll action is applied before the end of the operation, this // 'simulates' scrolling that position into view in a cheap way, so // that the effect of intermediate scroll commands is not ignored. function resolveScrollToPos(cm) { var range = cm.curOp.scrollToPos; if (range) { cm.curOp.scrollToPos = null; var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to); scrollToCoordsRange(cm, from, to, range.margin); } } function scrollToCoordsRange(cm, from, to, margin) { var sPos = calculateScrollPos(cm, { left: Math.min(from.left, to.left), top: Math.min(from.top, to.top) - margin, right: Math.max(from.right, to.right), bottom: Math.max(from.bottom, to.bottom) + margin }); scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop); } // Sync the scrollable area and scrollbars, ensure the viewport // covers the visible area. function updateScrollTop(cm, val) { if (Math.abs(cm.doc.scrollTop - val) < 2) { return } if (!gecko) { updateDisplaySimple(cm, {top: val}); } setScrollTop(cm, val, true); if (gecko) { updateDisplaySimple(cm); } startWorker(cm, 100); } function setScrollTop(cm, val, forceScroll) { val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val); if (cm.display.scroller.scrollTop == val && !forceScroll) { return } cm.doc.scrollTop = val; cm.display.scrollbars.setScrollTop(val); if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; } } // Sync scroller and scrollbar, ensure the gutter elements are // aligned. function setScrollLeft(cm, val, isScroller, forceScroll) { val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth); if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return } cm.doc.scrollLeft = val; alignHorizontally(cm); if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; } cm.display.scrollbars.setScrollLeft(val); } // SCROLLBARS // Prepare DOM reads needed to update the scrollbars. Done in one // shot to minimize update/measure roundtrips. function measureForScrollbars(cm) { var d = cm.display, gutterW = d.gutters.offsetWidth; var docH = Math.round(cm.doc.height + paddingVert(cm.display)); return { clientHeight: d.scroller.clientHeight, viewHeight: d.wrapper.clientHeight, scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth, viewWidth: d.wrapper.clientWidth, barLeft: cm.options.fixedGutter ? gutterW : 0, docHeight: docH, scrollHeight: docH + scrollGap(cm) + d.barHeight, nativeBarWidth: d.nativeBarWidth, gutterWidth: gutterW } } var NativeScrollbars = function(place, scroll, cm) { this.cm = cm; var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar"); var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar"); vert.tabIndex = horiz.tabIndex = -1; place(vert); place(horiz); on(vert, "scroll", function () { if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); } }); on(horiz, "scroll", function () { if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); } }); this.checkedZeroWidth = false; // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; } }; NativeScrollbars.prototype.update = function (measure) { var needsH = measure.scrollWidth > measure.clientWidth + 1; var needsV = measure.scrollHeight > measure.clientHeight + 1; var sWidth = measure.nativeBarWidth; if (needsV) { this.vert.style.display = "block"; this.vert.style.bottom = needsH ? sWidth + "px" : "0"; var totalHeight = measure.viewHeight - (needsH ? sWidth : 0); // A bug in IE8 can cause this value to be negative, so guard it. this.vert.firstChild.style.height = Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"; } else { this.vert.style.display = ""; this.vert.firstChild.style.height = "0"; } if (needsH) { this.horiz.style.display = "block"; this.horiz.style.right = needsV ? sWidth + "px" : "0"; this.horiz.style.left = measure.barLeft + "px"; var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0); this.horiz.firstChild.style.width = Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px"; } else { this.horiz.style.display = ""; this.horiz.firstChild.style.width = "0"; } if (!this.checkedZeroWidth && measure.clientHeight > 0) { if (sWidth == 0) { this.zeroWidthHack(); } this.checkedZeroWidth = true; } return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0} }; NativeScrollbars.prototype.setScrollLeft = function (pos) { if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; } if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); } }; NativeScrollbars.prototype.setScrollTop = function (pos) { if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; } if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); } }; NativeScrollbars.prototype.zeroWidthHack = function () { var w = mac && !mac_geMountainLion ? "12px" : "18px"; this.horiz.style.height = this.vert.style.width = w; this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"; this.disableHoriz = new Delayed; this.disableVert = new Delayed; }; NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) { bar.style.pointerEvents = "auto"; function maybeDisable() { // To find out whether the scrollbar is still visible, we // check whether the element under the pixel in the bottom // right corner of the scrollbar box is the scrollbar box // itself (when the bar is still visible) or its filler child // (when the bar is hidden). If it is still visible, we keep // it enabled, if it's hidden, we disable pointer events. var box = bar.getBoundingClientRect(); var elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2) : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1); if (elt != bar) { bar.style.pointerEvents = "none"; } else { delay.set(1000, maybeDisable); } } delay.set(1000, maybeDisable); }; NativeScrollbars.prototype.clear = function () { var parent = this.horiz.parentNode; parent.removeChild(this.horiz); parent.removeChild(this.vert); }; var NullScrollbars = function () {}; NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} }; NullScrollbars.prototype.setScrollLeft = function () {}; NullScrollbars.prototype.setScrollTop = function () {}; NullScrollbars.prototype.clear = function () {}; function updateScrollbars(cm, measure) { if (!measure) { measure = measureForScrollbars(cm); } var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight; updateScrollbarsInner(cm, measure); for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { if (startWidth != cm.display.barWidth && cm.options.lineWrapping) { updateHeightsInViewport(cm); } updateScrollbarsInner(cm, measureForScrollbars(cm)); startWidth = cm.display.barWidth; startHeight = cm.display.barHeight; } } // Re-synchronize the fake scrollbars with the actual size of the // content. function updateScrollbarsInner(cm, measure) { var d = cm.display; var sizes = d.scrollbars.update(measure); d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"; d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"; d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"; if (sizes.right && sizes.bottom) { d.scrollbarFiller.style.display = "block"; d.scrollbarFiller.style.height = sizes.bottom + "px"; d.scrollbarFiller.style.width = sizes.right + "px"; } else { d.scrollbarFiller.style.display = ""; } if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) { d.gutterFiller.style.display = "block"; d.gutterFiller.style.height = sizes.bottom + "px"; d.gutterFiller.style.width = measure.gutterWidth + "px"; } else { d.gutterFiller.style.display = ""; } } var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}; function initScrollbars(cm) { if (cm.display.scrollbars) { cm.display.scrollbars.clear(); if (cm.display.scrollbars.addClass) { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); } } cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) { cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller); // Prevent clicks in the scrollbars from killing focus on(node, "mousedown", function () { if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); } }); node.setAttribute("cm-not-content", "true"); }, function (pos, axis) { if (axis == "horizontal") { setScrollLeft(cm, pos); } else { updateScrollTop(cm, pos); } }, cm); if (cm.display.scrollbars.addClass) { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); } } // Operations are used to wrap a series of changes to the editor // state in such a way that each change won't have to update the // cursor and display (which would be awkward, slow, and // error-prone). Instead, display updates are batched and then all // combined and executed at once. var nextOpId = 0; // Start a new operation. function startOperation(cm) { cm.curOp = { cm: cm, viewChanged: false, // Flag that indicates that lines might need to be redrawn startHeight: cm.doc.height, // Used to detect need to update scrollbar forceUpdate: false, // Used to force a redraw updateInput: 0, // Whether to reset the input textarea typing: false, // Whether this reset should be careful to leave existing text (for compositing) changeObjs: null, // Accumulated changes, for firing change events cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already selectionChanged: false, // Whether the selection needs to be redrawn updateMaxLine: false, // Set when the widest line needs to be determined anew scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet scrollToPos: null, // Used to scroll to a specific position focus: false, id: ++nextOpId // Unique ID }; pushOperation(cm.curOp); } // Finish an operation, updating the display and signalling delayed events function endOperation(cm) { var op = cm.curOp; if (op) { finishOperation(op, function (group) { for (var i = 0; i < group.ops.length; i++) { group.ops[i].cm.curOp = null; } endOperations(group); }); } } // The DOM updates done when an operation finishes are batched so // that the minimum number of relayouts are required. function endOperations(group) { var ops = group.ops; for (var i = 0; i < ops.length; i++) // Read DOM { endOperation_R1(ops[i]); } for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe) { endOperation_W1(ops[i$1]); } for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM { endOperation_R2(ops[i$2]); } for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe) { endOperation_W2(ops[i$3]); } for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM { endOperation_finish(ops[i$4]); } } function endOperation_R1(op) { var cm = op.cm, display = cm.display; maybeClipScrollbars(cm); if (op.updateMaxLine) { findMaxLine(cm); } op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null || op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || op.scrollToPos.to.line >= display.viewTo) || display.maxLineChanged && cm.options.lineWrapping; op.update = op.mustUpdate && new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate); } function endOperation_W1(op) { op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update); } function endOperation_R2(op) { var cm = op.cm, display = cm.display; if (op.updatedDisplay) { updateHeightsInViewport(cm); } op.barMeasure = measureForScrollbars(cm); // If the max line changed since it was last measured, measure it, // and ensure the document's width matches it. // updateDisplay_W2 will use these properties to do the actual resizing if (display.maxLineChanged && !cm.options.lineWrapping) { op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3; cm.display.sizerWidth = op.adjustWidthTo; op.barMeasure.scrollWidth = Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth); op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm)); } if (op.updatedDisplay || op.selectionChanged) { op.preparedSelection = display.input.prepareSelection(); } } function endOperation_W2(op) { var cm = op.cm; if (op.adjustWidthTo != null) { cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"; if (op.maxScrollLeft < cm.doc.scrollLeft) { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); } cm.display.maxLineChanged = false; } var takeFocus = op.focus && op.focus == activeElt(); if (op.preparedSelection) { cm.display.input.showSelection(op.preparedSelection, takeFocus); } if (op.updatedDisplay || op.startHeight != cm.doc.height) { updateScrollbars(cm, op.barMeasure); } if (op.updatedDisplay) { setDocumentHeight(cm, op.barMeasure); } if (op.selectionChanged) { restartBlink(cm); } if (cm.state.focused && op.updateInput) { cm.display.input.reset(op.typing); } if (takeFocus) { ensureFocus(op.cm); } } function endOperation_finish(op) { var cm = op.cm, display = cm.display, doc = cm.doc; if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); } // Abort mouse wheel delta measurement, when scrolling explicitly if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) { display.wheelStartX = display.wheelStartY = null; } // Propagate the scroll position to the actual DOM scroller if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); } if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); } // If we need to scroll a specific position into view, do so. if (op.scrollToPos) { var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin); maybeScrollWindow(cm, rect); } // Fire events for markers that are hidden/unidden by editing or // undoing var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; if (hidden) { for (var i = 0; i < hidden.length; ++i) { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } } if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1) { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } } if (display.wrapper.offsetHeight) { doc.scrollTop = cm.display.scroller.scrollTop; } // Fire change events, and delayed event handlers if (op.changeObjs) { signal(cm, "changes", cm, op.changeObjs); } if (op.update) { op.update.finish(); } } // Run the given function in an operation function runInOp(cm, f) { if (cm.curOp) { return f() } startOperation(cm); try { return f() } finally { endOperation(cm); } } // Wraps a function in an operation. Returns the wrapped function. function operation(cm, f) { return function() { if (cm.curOp) { return f.apply(cm, arguments) } startOperation(cm); try { return f.apply(cm, arguments) } finally { endOperation(cm); } } } // Used to add methods to editor and doc instances, wrapping them in // operations. function methodOp(f) { return function() { if (this.curOp) { return f.apply(this, arguments) } startOperation(this); try { return f.apply(this, arguments) } finally { endOperation(this); } } } function docMethodOp(f) { return function() { var cm = this.cm; if (!cm || cm.curOp) { return f.apply(this, arguments) } startOperation(cm); try { return f.apply(this, arguments) } finally { endOperation(cm); } } } // HIGHLIGHT WORKER function startWorker(cm, time) { if (cm.doc.highlightFrontier < cm.display.viewTo) { cm.state.highlight.set(time, bind(highlightWorker, cm)); } } function highlightWorker(cm) { var doc = cm.doc; if (doc.highlightFrontier >= cm.display.viewTo) { return } var end = +new Date + cm.options.workTime; var context = getContextBefore(cm, doc.highlightFrontier); var changedLines = []; doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) { if (context.line >= cm.display.viewFrom) { // Visible var oldStyles = line.styles; var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null; var highlighted = highlightLine(cm, line, context, true); if (resetState) { context.state = resetState; } line.styles = highlighted.styles; var oldCls = line.styleClasses, newCls = highlighted.classes; if (newCls) { line.styleClasses = newCls; } else if (oldCls) { line.styleClasses = null; } var ischange = !oldStyles || oldStyles.length != line.styles.length || oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass); for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; } if (ischange) { changedLines.push(context.line); } line.stateAfter = context.save(); context.nextLine(); } else { if (line.text.length <= cm.options.maxHighlightLength) { processLine(cm, line.text, context); } line.stateAfter = context.line % 5 == 0 ? context.save() : null; context.nextLine(); } if (+new Date > end) { startWorker(cm, cm.options.workDelay); return true } }); doc.highlightFrontier = context.line; doc.modeFrontier = Math.max(doc.modeFrontier, context.line); if (changedLines.length) { runInOp(cm, function () { for (var i = 0; i < changedLines.length; i++) { regLineChange(cm, changedLines[i], "text"); } }); } } // DISPLAY DRAWING var DisplayUpdate = function(cm, viewport, force) { var display = cm.display; this.viewport = viewport; // Store some values that we'll need later (but don't want to force a relayout for) this.visible = visibleLines(display, cm.doc, viewport); this.editorIsHidden = !display.wrapper.offsetWidth; this.wrapperHeight = display.wrapper.clientHeight; this.wrapperWidth = display.wrapper.clientWidth; this.oldDisplayWidth = displayWidth(cm); this.force = force; this.dims = getDimensions(cm); this.events = []; }; DisplayUpdate.prototype.signal = function (emitter, type) { if (hasHandler(emitter, type)) { this.events.push(arguments); } }; DisplayUpdate.prototype.finish = function () { for (var i = 0; i < this.events.length; i++) { signal.apply(null, this.events[i]); } }; function maybeClipScrollbars(cm) { var display = cm.display; if (!display.scrollbarsClipped && display.scroller.offsetWidth) { display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth; display.heightForcer.style.height = scrollGap(cm) + "px"; display.sizer.style.marginBottom = -display.nativeBarWidth + "px"; display.sizer.style.borderRightWidth = scrollGap(cm) + "px"; display.scrollbarsClipped = true; } } function selectionSnapshot(cm) { if (cm.hasFocus()) { return null } var active = activeElt(); if (!active || !contains(cm.display.lineDiv, active)) { return null } var result = {activeElt: active}; if (window.getSelection) { var sel = window.getSelection(); if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) { result.anchorNode = sel.anchorNode; result.anchorOffset = sel.anchorOffset; result.focusNode = sel.focusNode; result.focusOffset = sel.focusOffset; } } return result } function restoreSelection(snapshot) { if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return } snapshot.activeElt.focus(); if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) { var sel = window.getSelection(), range = document.createRange(); range.setEnd(snapshot.anchorNode, snapshot.anchorOffset); range.collapse(false); sel.removeAllRanges(); sel.addRange(range); sel.extend(snapshot.focusNode, snapshot.focusOffset); } } // Does the actual updating of the line display. Bails out // (returning false) when there is nothing to be done and forced is // false. function updateDisplayIfNeeded(cm, update) { var display = cm.display, doc = cm.doc; if (update.editorIsHidden) { resetView(cm); return false } // Bail out if the visible area is already rendered and nothing changed. if (!update.force && update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo && (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) && display.renderedView == display.view && countDirtyView(cm) == 0) { return false } if (maybeUpdateLineNumberWidth(cm)) { resetView(cm); update.dims = getDimensions(cm); } // Compute a suitable new viewport (from & to) var end = doc.first + doc.size; var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first); var to = Math.min(end, update.visible.to + cm.options.viewportMargin); if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); } if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); } if (sawCollapsedSpans) { from = visualLineNo(cm.doc, from); to = visualLineEndNo(cm.doc, to); } var different = from != display.viewFrom || to != display.viewTo || display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth; adjustView(cm, from, to); display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)); // Position the mover div to align with the current scroll position cm.display.mover.style.top = display.viewOffset + "px"; var toUpdate = countDirtyView(cm); if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view && (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)) { return false } // For big changes, we hide the enclosing element during the // update, since that speeds up the operations on most browsers. var selSnapshot = selectionSnapshot(cm); if (toUpdate > 4) { display.lineDiv.style.display = "none"; } patchDisplay(cm, display.updateLineNumbers, update.dims); if (toUpdate > 4) { display.lineDiv.style.display = ""; } display.renderedView = display.view; // There might have been a widget with a focused element that got // hidden or updated, if so re-focus it. restoreSelection(selSnapshot); // Prevent selection and cursors from interfering with the scroll // width and height. removeChildren(display.cursorDiv); removeChildren(display.selectionDiv); display.gutters.style.height = display.sizer.style.minHeight = 0; if (different) { display.lastWrapHeight = update.wrapperHeight; display.lastWrapWidth = update.wrapperWidth; startWorker(cm, 400); } display.updateLineNumbers = null; return true } function postUpdateDisplay(cm, update) { var viewport = update.viewport; for (var first = true;; first = false) { if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) { // Clip forced viewport to actual scrollable area. if (viewport && viewport.top != null) { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; } // Updated line heights might result in the drawn area not // actually covering the viewport. Keep looping until it does. update.visible = visibleLines(cm.display, cm.doc, viewport); if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) { break } } if (!updateDisplayIfNeeded(cm, update)) { break } updateHeightsInViewport(cm); var barMeasure = measureForScrollbars(cm); updateSelection(cm); updateScrollbars(cm, barMeasure); setDocumentHeight(cm, barMeasure); update.force = false; } update.signal(cm, "update", cm); if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) { update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo); cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo; } } function updateDisplaySimple(cm, viewport) { var update = new DisplayUpdate(cm, viewport); if (updateDisplayIfNeeded(cm, update)) { updateHeightsInViewport(cm); postUpdateDisplay(cm, update); var barMeasure = measureForScrollbars(cm); updateSelection(cm); updateScrollbars(cm, barMeasure); setDocumentHeight(cm, barMeasure); update.finish(); } } // Sync the actual display DOM structure with display.view, removing // nodes for lines that are no longer in view, and creating the ones // that are not there yet, and updating the ones that are out of // date. function patchDisplay(cm, updateNumbersFrom, dims) { var display = cm.display, lineNumbers = cm.options.lineNumbers; var container = display.lineDiv, cur = container.firstChild; function rm(node) { var next = node.nextSibling; // Works around a throw-scroll bug in OS X Webkit if (webkit && mac && cm.display.currentWheelTarget == node) { node.style.display = "none"; } else { node.parentNode.removeChild(node); } return next } var view = display.view, lineN = display.viewFrom; // Loop over the elements in the view, syncing cur (the DOM nodes // in display.lineDiv) with the view as we go. for (var i = 0; i < view.length; i++) { var lineView = view[i]; if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet var node = buildLineElement(cm, lineView, lineN, dims); container.insertBefore(node, cur); } else { // Already drawn while (cur != lineView.node) { cur = rm(cur); } var updateNumber = lineNumbers && updateNumbersFrom != null && updateNumbersFrom <= lineN && lineView.lineNumber; if (lineView.changes) { if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; } updateLineForChanges(cm, lineView, lineN, dims); } if (updateNumber) { removeChildren(lineView.lineNumber); lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN))); } cur = lineView.node.nextSibling; } lineN += lineView.size; } while (cur) { cur = rm(cur); } } function updateGutterSpace(display) { var width = display.gutters.offsetWidth; display.sizer.style.marginLeft = width + "px"; } function setDocumentHeight(cm, measure) { cm.display.sizer.style.minHeight = measure.docHeight + "px"; cm.display.heightForcer.style.top = measure.docHeight + "px"; cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"; } // Re-align line numbers and gutter marks to compensate for // horizontal scrolling. function alignHorizontally(cm) { var display = cm.display, view = display.view; if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return } var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft; var gutterW = display.gutters.offsetWidth, left = comp + "px"; for (var i = 0; i < view.length; i++) { if (!view[i].hidden) { if (cm.options.fixedGutter) { if (view[i].gutter) { view[i].gutter.style.left = left; } if (view[i].gutterBackground) { view[i].gutterBackground.style.left = left; } } var align = view[i].alignable; if (align) { for (var j = 0; j < align.length; j++) { align[j].style.left = left; } } } } if (cm.options.fixedGutter) { display.gutters.style.left = (comp + gutterW) + "px"; } } // Used to ensure that the line number gutter is still the right // size for the current document size. Returns true when an update // is needed. function maybeUpdateLineNumberWidth(cm) { if (!cm.options.lineNumbers) { return false } var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display; if (last.length != display.lineNumChars) { var test = display.measure.appendChild(elt("div", [elt("div", last)], "CodeMirror-linenumber CodeMirror-gutter-elt")); var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW; display.lineGutter.style.width = ""; display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1; display.lineNumWidth = display.lineNumInnerWidth + padding; display.lineNumChars = display.lineNumInnerWidth ? last.length : -1; display.lineGutter.style.width = display.lineNumWidth + "px"; updateGutterSpace(cm.display); return true } return false } function getGutters(gutters, lineNumbers) { var result = [], sawLineNumbers = false; for (var i = 0; i < gutters.length; i++) { var name = gutters[i], style = null; if (typeof name != "string") { style = name.style; name = name.className; } if (name == "CodeMirror-linenumbers") { if (!lineNumbers) { continue } else { sawLineNumbers = true; } } result.push({className: name, style: style}); } if (lineNumbers && !sawLineNumbers) { result.push({className: "CodeMirror-linenumbers", style: null}); } return result } // Rebuild the gutter elements, ensure the margin to the left of the // code matches their width. function renderGutters(display) { var gutters = display.gutters, specs = display.gutterSpecs; removeChildren(gutters); display.lineGutter = null; for (var i = 0; i < specs.length; ++i) { var ref = specs[i]; var className = ref.className; var style = ref.style; var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className)); if (style) { gElt.style.cssText = style; } if (className == "CodeMirror-linenumbers") { display.lineGutter = gElt; gElt.style.width = (display.lineNumWidth || 1) + "px"; } } gutters.style.display = specs.length ? "" : "none"; updateGutterSpace(display); } function updateGutters(cm) { renderGutters(cm.display); regChange(cm); alignHorizontally(cm); } // The display handles the DOM integration, both for input reading // and content drawing. It holds references to DOM nodes and // display-related state. function Display(place, doc, input, options) { var d = this; this.input = input; // Covers bottom-right square when both scrollbars are present. d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); d.scrollbarFiller.setAttribute("cm-not-content", "true"); // Covers bottom of gutter when coverGutterNextToScrollbar is on // and h scrollbar is present. d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler"); d.gutterFiller.setAttribute("cm-not-content", "true"); // Will contain the actual code, positioned to cover the viewport. d.lineDiv = eltP("div", null, "CodeMirror-code"); // Elements are added to these to represent selection and cursors. d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); d.cursorDiv = elt("div", null, "CodeMirror-cursors"); // A visibility: hidden element used to find the size of things. d.measure = elt("div", null, "CodeMirror-measure"); // When lines outside of the viewport are measured, they are drawn in this. d.lineMeasure = elt("div", null, "CodeMirror-measure"); // Wraps everything that needs to exist inside the vertically-padded coordinate system d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv], null, "position: relative; outline: none"); var lines = eltP("div", [d.lineSpace], "CodeMirror-lines"); // Moved around its parent to cover visible view. d.mover = elt("div", [lines], null, "position: relative"); // Set to the height of the document, allowing scrolling. d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); d.sizerWidth = null; // Behavior of elts with overflow: auto and padding is // inconsistent across browsers. This is used to ensure the // scrollable area is big enough. d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;"); // Will contain the gutters, if any. d.gutters = elt("div", null, "CodeMirror-gutters"); d.lineGutter = null; // Actual scrollable element. d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll"); d.scroller.setAttribute("tabIndex", "-1"); // The element in which the editor lives. d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror"); // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported) if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; } if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; } if (place) { if (place.appendChild) { place.appendChild(d.wrapper); } else { place(d.wrapper); } } // Current rendered range (may be bigger than the view window). d.viewFrom = d.viewTo = doc.first; d.reportedViewFrom = d.reportedViewTo = doc.first; // Information about the rendered lines. d.view = []; d.renderedView = null; // Holds info about a single rendered line when it was rendered // for measurement, while not in view. d.externalMeasured = null; // Empty space (in pixels) above the view d.viewOffset = 0; d.lastWrapHeight = d.lastWrapWidth = 0; d.updateLineNumbers = null; d.nativeBarWidth = d.barHeight = d.barWidth = 0; d.scrollbarsClipped = false; // Used to only resize the line number gutter when necessary (when // the amount of lines crosses a boundary that makes its width change) d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null; // Set to true when a non-horizontal-scrolling line widget is // added. As an optimization, line widget aligning is skipped when // this is false. d.alignWidgets = false; d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; // Tracks the maximum line length so that the horizontal scrollbar // can be kept static when scrolling. d.maxLine = null; d.maxLineLength = 0; d.maxLineChanged = false; // Used for measuring wheel scrolling granularity d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; // True when shift is held down. d.shift = false; // Used to track whether anything happened since the context menu // was opened. d.selForContextMenu = null; d.activeTouch = null; d.gutterSpecs = getGutters(options.gutters, options.lineNumbers); renderGutters(d); input.init(d); } // Since the delta values reported on mouse wheel events are // unstandardized between browsers and even browser versions, and // generally horribly unpredictable, this code starts by measuring // the scroll effect that the first few mouse wheel events have, // and, from that, detects the way it can convert deltas to pixel // offsets afterwards. // // The reason we want to know the amount a wheel event will scroll // is that it gives us a chance to update the display before the // actual scrolling happens, reducing flickering. var wheelSamples = 0, wheelPixelsPerUnit = null; // Fill in a browser-detected starting value on browsers where we // know one. These don't have to be accurate -- the result of them // being wrong would just be a slight flicker on the first wheel // scroll (if it is large enough). if (ie) { wheelPixelsPerUnit = -.53; } else if (gecko) { wheelPixelsPerUnit = 15; } else if (chrome) { wheelPixelsPerUnit = -.7; } else if (safari) { wheelPixelsPerUnit = -1/3; } function wheelEventDelta(e) { var dx = e.wheelDeltaX, dy = e.wheelDeltaY; if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; } if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; } else if (dy == null) { dy = e.wheelDelta; } return {x: dx, y: dy} } function wheelEventPixels(e) { var delta = wheelEventDelta(e); delta.x *= wheelPixelsPerUnit; delta.y *= wheelPixelsPerUnit; return delta } function onScrollWheel(cm, e) { var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y; var display = cm.display, scroll = display.scroller; // Quit if there's nothing to scroll here var canScrollX = scroll.scrollWidth > scroll.clientWidth; var canScrollY = scroll.scrollHeight > scroll.clientHeight; if (!(dx && canScrollX || dy && canScrollY)) { return } // Webkit browsers on OS X abort momentum scrolls when the target // of the scroll event is removed from the scrollable element. // This hack (see related code in patchDisplay) makes sure the // element is kept around. if (dy && mac && webkit) { outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { for (var i = 0; i < view.length; i++) { if (view[i].node == cur) { cm.display.currentWheelTarget = cur; break outer } } } } // On some browsers, horizontal scrolling will cause redraws to // happen before the gutter has been realigned, causing it to // wriggle around in a most unseemly way. When we have an // estimated pixels/delta value, we just handle horizontal // scrolling entirely here. It'll be slightly off from native, but // better than glitching out. if (dx && !gecko && !presto && wheelPixelsPerUnit != null) { if (dy && canScrollY) { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); } setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit)); // Only prevent default scrolling if vertical scrolling is // actually possible. Otherwise, it causes vertical scroll // jitter on OSX trackpads when deltaX is small and deltaY // is large (issue #3579) if (!dy || (dy && canScrollY)) { e_preventDefault(e); } display.wheelStartX = null; // Abort measurement, if in progress return } // 'Project' the visible viewport to cover the area that is being // scrolled into view (if we know enough to estimate it). if (dy && wheelPixelsPerUnit != null) { var pixels = dy * wheelPixelsPerUnit; var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; if (pixels < 0) { top = Math.max(0, top + pixels - 50); } else { bot = Math.min(cm.doc.height, bot + pixels + 50); } updateDisplaySimple(cm, {top: top, bottom: bot}); } if (wheelSamples < 20) { if (display.wheelStartX == null) { display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop; display.wheelDX = dx; display.wheelDY = dy; setTimeout(function () { if (display.wheelStartX == null) { return } var movedX = scroll.scrollLeft - display.wheelStartX; var movedY = scroll.scrollTop - display.wheelStartY; var sample = (movedY && display.wheelDY && movedY / display.wheelDY) || (movedX && display.wheelDX && movedX / display.wheelDX); display.wheelStartX = display.wheelStartY = null; if (!sample) { return } wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1); ++wheelSamples; }, 200); } else { display.wheelDX += dx; display.wheelDY += dy; } } } // Selection objects are immutable. A new one is created every time // the selection changes. A selection is one or more non-overlapping // (and non-touching) ranges, sorted, and an integer that indicates // which one is the primary selection (the one that's scrolled into // view, that getCursor returns, etc). var Selection = function(ranges, primIndex) { this.ranges = ranges; this.primIndex = primIndex; }; Selection.prototype.primary = function () { return this.ranges[this.primIndex] }; Selection.prototype.equals = function (other) { if (other == this) { return true } if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false } for (var i = 0; i < this.ranges.length; i++) { var here = this.ranges[i], there = other.ranges[i]; if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false } } return true }; Selection.prototype.deepCopy = function () { var out = []; for (var i = 0; i < this.ranges.length; i++) { out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); } return new Selection(out, this.primIndex) }; Selection.prototype.somethingSelected = function () { for (var i = 0; i < this.ranges.length; i++) { if (!this.ranges[i].empty()) { return true } } return false }; Selection.prototype.contains = function (pos, end) { if (!end) { end = pos; } for (var i = 0; i < this.ranges.length; i++) { var range = this.ranges[i]; if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) { return i } } return -1 }; var Range = function(anchor, head) { this.anchor = anchor; this.head = head; }; Range.prototype.from = function () { return minPos(this.anchor, this.head) }; Range.prototype.to = function () { return maxPos(this.anchor, this.head) }; Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch }; // Take an unsorted, potentially overlapping set of ranges, and // build a selection out of it. 'Consumes' ranges array (modifying // it). function normalizeSelection(cm, ranges, primIndex) { var mayTouch = cm && cm.options.selectionsMayTouch; var prim = ranges[primIndex]; ranges.sort(function (a, b) { return cmp(a.from(), b.from()); }); primIndex = indexOf(ranges, prim); for (var i = 1; i < ranges.length; i++) { var cur = ranges[i], prev = ranges[i - 1]; var diff = cmp(prev.to(), cur.from()); if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) { var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()); var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head; if (i <= primIndex) { --primIndex; } ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to)); } } return new Selection(ranges, primIndex) } function simpleSelection(anchor, head) { return new Selection([new Range(anchor, head || anchor)], 0) } // Compute the position of the end of a change (its 'to' property // refers to the pre-change end). function changeEnd(change) { if (!change.text) { return change.to } return Pos(change.from.line + change.text.length - 1, lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)) } // Adjust a position to refer to the post-change position of the // same text, or the end of the change if the change covers it. function adjustForChange(pos, change) { if (cmp(pos, change.from) < 0) { return pos } if (cmp(pos, change.to) <= 0) { return changeEnd(change) } var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; } return Pos(line, ch) } function computeSelAfterChange(doc, change) { var out = []; for (var i = 0; i < doc.sel.ranges.length; i++) { var range = doc.sel.ranges[i]; out.push(new Range(adjustForChange(range.anchor, change), adjustForChange(range.head, change))); } return normalizeSelection(doc.cm, out, doc.sel.primIndex) } function offsetPos(pos, old, nw) { if (pos.line == old.line) { return Pos(nw.line, pos.ch - old.ch + nw.ch) } else { return Pos(nw.line + (pos.line - old.line), pos.ch) } } // Used by replaceSelections to allow moving the selection to the // start or around the replaced test. Hint may be "start" or "around". function computeReplacedSel(doc, changes, hint) { var out = []; var oldPrev = Pos(doc.first, 0), newPrev = oldPrev; for (var i = 0; i < changes.length; i++) { var change = changes[i]; var from = offsetPos(change.from, oldPrev, newPrev); var to = offsetPos(changeEnd(change), oldPrev, newPrev); oldPrev = change.to; newPrev = to; if (hint == "around") { var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0; out[i] = new Range(inv ? to : from, inv ? from : to); } else { out[i] = new Range(from, from); } } return new Selection(out, doc.sel.primIndex) } // Used to get the editor into a consistent state again when options change. function loadMode(cm) { cm.doc.mode = getMode(cm.options, cm.doc.modeOption); resetModeState(cm); } function resetModeState(cm) { cm.doc.iter(function (line) { if (line.stateAfter) { line.stateAfter = null; } if (line.styles) { line.styles = null; } }); cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first; startWorker(cm, 100); cm.state.modeGen++; if (cm.curOp) { regChange(cm); } } // DOCUMENT DATA STRUCTURE // By default, updates that start and end at the beginning of a line // are treated specially, in order to make the association of line // widgets and marker elements with the text behave more intuitive. function isWholeLineUpdate(doc, change) { return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" && (!doc.cm || doc.cm.options.wholeLineUpdateBefore) } // Perform a change on the document data structure. function updateDoc(doc, change, markedSpans, estimateHeight) { function spansFor(n) {return markedSpans ? markedSpans[n] : null} function update(line, text, spans) { updateLine(line, text, spans, estimateHeight); signalLater(line, "change", line, change); } function linesFor(start, end) { var result = []; for (var i = start; i < end; ++i) { result.push(new Line(text[i], spansFor(i), estimateHeight)); } return result } var from = change.from, to = change.to, text = change.text; var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line; // Adjust the line structure if (change.full) { doc.insert(0, linesFor(0, text.length)); doc.remove(text.length, doc.size - text.length); } else if (isWholeLineUpdate(doc, change)) { // This is a whole-line replace. Treated specially to make // sure line objects move the way they are supposed to. var added = linesFor(0, text.length - 1); update(lastLine, lastLine.text, lastSpans); if (nlines) { doc.remove(from.line, nlines); } if (added.length) { doc.insert(from.line, added); } } else if (firstLine == lastLine) { if (text.length == 1) { update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); } else { var added$1 = linesFor(1, text.length - 1); added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)); update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); doc.insert(from.line + 1, added$1); } } else if (text.length == 1) { update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0)); doc.remove(from.line + 1, nlines); } else { update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans); var added$2 = linesFor(1, text.length - 1); if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); } doc.insert(from.line + 1, added$2); } signalLater(doc, "change", doc, change); } // Call f for all linked documents. function linkedDocs(doc, f, sharedHistOnly) { function propagate(doc, skip, sharedHist) { if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) { var rel = doc.linked[i]; if (rel.doc == skip) { continue } var shared = sharedHist && rel.sharedHist; if (sharedHistOnly && !shared) { continue } f(rel.doc, shared); propagate(rel.doc, doc, shared); } } } propagate(doc, null, true); } // Attach a document to an editor. function attachDoc(cm, doc) { if (doc.cm) { throw new Error("This document is already in use.") } cm.doc = doc; doc.cm = cm; estimateLineHeights(cm); loadMode(cm); setDirectionClass(cm); if (!cm.options.lineWrapping) { findMaxLine(cm); } cm.options.mode = doc.modeOption; regChange(cm); } function setDirectionClass(cm) { (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl"); } function directionChanged(cm) { runInOp(cm, function () { setDirectionClass(cm); regChange(cm); }); } function History(startGen) { // Arrays of change events and selections. Doing something adds an // event to done and clears undo. Undoing moves events from done // to undone, redoing moves them in the other direction. this.done = []; this.undone = []; this.undoDepth = Infinity; // Used to track when changes can be merged into a single undo // event this.lastModTime = this.lastSelTime = 0; this.lastOp = this.lastSelOp = null; this.lastOrigin = this.lastSelOrigin = null; // Used by the isClean() method this.generation = this.maxGeneration = startGen || 1; } // Create a history change event from an updateDoc-style change // object. function historyChangeFromChange(doc, change) { var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}; attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true); return histChange } // Pop all selection events off the end of a history array. Stop at // a change event. function clearSelectionEvents(array) { while (array.length) { var last = lst(array); if (last.ranges) { array.pop(); } else { break } } } // Find the top change event in the history. Pop off selection // events that are in the way. function lastChangeEvent(hist, force) { if (force) { clearSelectionEvents(hist.done); return lst(hist.done) } else if (hist.done.length && !lst(hist.done).ranges) { return lst(hist.done) } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) { hist.done.pop(); return lst(hist.done) } } // Register a change in the history. Merges changes that are within // a single operation, or are close together with an origin that // allows merging (starting with "+") into a single event. function addChangeToHistory(doc, change, selAfter, opId) { var hist = doc.history; hist.undone.length = 0; var time = +new Date, cur; var last; if ((hist.lastOp == opId || hist.lastOrigin == change.origin && change.origin && ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) || change.origin.charAt(0) == "*")) && (cur = lastChangeEvent(hist, hist.lastOp == opId))) { // Merge this change into the last event last = lst(cur.changes); if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { // Optimized case for simple insertion -- don't want to add // new changesets for every character typed last.to = changeEnd(change); } else { // Add new sub-event cur.changes.push(historyChangeFromChange(doc, change)); } } else { // Can not be merged, start a new event. var before = lst(hist.done); if (!before || !before.ranges) { pushSelectionToHistory(doc.sel, hist.done); } cur = {changes: [historyChangeFromChange(doc, change)], generation: hist.generation}; hist.done.push(cur); while (hist.done.length > hist.undoDepth) { hist.done.shift(); if (!hist.done[0].ranges) { hist.done.shift(); } } } hist.done.push(selAfter); hist.generation = ++hist.maxGeneration; hist.lastModTime = hist.lastSelTime = time; hist.lastOp = hist.lastSelOp = opId; hist.lastOrigin = hist.lastSelOrigin = change.origin; if (!last) { signal(doc, "historyAdded"); } } function selectionEventCanBeMerged(doc, origin, prev, sel) { var ch = origin.charAt(0); return ch == "*" || ch == "+" && prev.ranges.length == sel.ranges.length && prev.somethingSelected() == sel.somethingSelected() && new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500) } // Called whenever the selection changes, sets the new selection as // the pending selection in the history, and pushes the old pending // selection into the 'done' array when it was significantly // different (in number of selected ranges, emptiness, or time). function addSelectionToHistory(doc, sel, opId, options) { var hist = doc.history, origin = options && options.origin; // A new event is started when the previous origin does not match // the current, or the origins don't allow matching. Origins // starting with * are always merged, those starting with + are // merged when similar and close together in time. if (opId == hist.lastSelOp || (origin && hist.lastSelOrigin == origin && (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) { hist.done[hist.done.length - 1] = sel; } else { pushSelectionToHistory(sel, hist.done); } hist.lastSelTime = +new Date; hist.lastSelOrigin = origin; hist.lastSelOp = opId; if (options && options.clearRedo !== false) { clearSelectionEvents(hist.undone); } } function pushSelectionToHistory(sel, dest) { var top = lst(dest); if (!(top && top.ranges && top.equals(sel))) { dest.push(sel); } } // Used to store marked span information in the history. function attachLocalSpans(doc, change, from, to) { var existing = change["spans_" + doc.id], n = 0; doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) { if (line.markedSpans) { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; } ++n; }); } // When un/re-doing restores text containing marked spans, those // that have been explicitly cleared should not be restored. function removeClearedSpans(spans) { if (!spans) { return null } var out; for (var i = 0; i < spans.length; ++i) { if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } } else if (out) { out.push(spans[i]); } } return !out ? spans : out.length ? out : null } // Retrieve and filter the old marked spans stored in a change event. function getOldSpans(doc, change) { var found = change["spans_" + doc.id]; if (!found) { return null } var nw = []; for (var i = 0; i < change.text.length; ++i) { nw.push(removeClearedSpans(found[i])); } return nw } // Used for un/re-doing changes from the history. Combines the // result of computing the existing spans with the set of spans that // existed in the history (so that deleting around a span and then // undoing brings back the span). function mergeOldSpans(doc, change) { var old = getOldSpans(doc, change); var stretched = stretchSpansOverChange(doc, change); if (!old) { return stretched } if (!stretched) { return old } for (var i = 0; i < old.length; ++i) { var oldCur = old[i], stretchCur = stretched[i]; if (oldCur && stretchCur) { spans: for (var j = 0; j < stretchCur.length; ++j) { var span = stretchCur[j]; for (var k = 0; k < oldCur.length; ++k) { if (oldCur[k].marker == span.marker) { continue spans } } oldCur.push(span); } } else if (stretchCur) { old[i] = stretchCur; } } return old } // Used both to provide a JSON-safe object in .getHistory, and, when // detaching a document, to split the history in two function copyHistoryArray(events, newGroup, instantiateSel) { var copy = []; for (var i = 0; i < events.length; ++i) { var event = events[i]; if (event.ranges) { copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event); continue } var changes = event.changes, newChanges = []; copy.push({changes: newChanges}); for (var j = 0; j < changes.length; ++j) { var change = changes[j], m = (void 0); newChanges.push({from: change.from, to: change.to, text: change.text}); if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) { if (indexOf(newGroup, Number(m[1])) > -1) { lst(newChanges)[prop] = change[prop]; delete change[prop]; } } } } } } return copy } // The 'scroll' parameter given to many of these indicated whether // the new cursor position should be scrolled into view after // modifying the selection. // If shift is held or the extend flag is set, extends a range to // include a given position (and optionally a second position). // Otherwise, simply returns the range between the given positions. // Used for cursor motion and such. function extendRange(range, head, other, extend) { if (extend) { var anchor = range.anchor; if (other) { var posBefore = cmp(head, anchor) < 0; if (posBefore != (cmp(other, anchor) < 0)) { anchor = head; head = other; } else if (posBefore != (cmp(head, other) < 0)) { head = other; } } return new Range(anchor, head) } else { return new Range(other || head, head) } } // Extend the primary selection range, discard the rest. function extendSelection(doc, head, other, options, extend) { if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); } setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options); } // Extend all selections (pos is an array of selections with length // equal the number of selections) function extendSelections(doc, heads, options) { var out = []; var extend = doc.cm && (doc.cm.display.shift || doc.extend); for (var i = 0; i < doc.sel.ranges.length; i++) { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); } var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex); setSelection(doc, newSel, options); } // Updates a single range in the selection. function replaceOneSelection(doc, i, range, options) { var ranges = doc.sel.ranges.slice(0); ranges[i] = range; setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options); } // Reset the selection to a single range. function setSimpleSelection(doc, anchor, head, options) { setSelection(doc, simpleSelection(anchor, head), options); } // Give beforeSelectionChange handlers a change to influence a // selection update. function filterSelectionChange(doc, sel, options) { var obj = { ranges: sel.ranges, update: function(ranges) { this.ranges = []; for (var i = 0; i < ranges.length; i++) { this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), clipPos(doc, ranges[i].head)); } }, origin: options && options.origin }; signal(doc, "beforeSelectionChange", doc, obj); if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); } if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) } else { return sel } } function setSelectionReplaceHistory(doc, sel, options) { var done = doc.history.done, last = lst(done); if (last && last.ranges) { done[done.length - 1] = sel; setSelectionNoUndo(doc, sel, options); } else { setSelection(doc, sel, options); } } // Set a new selection. function setSelection(doc, sel, options) { setSelectionNoUndo(doc, sel, options); addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options); } function setSelectionNoUndo(doc, sel, options) { if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) { sel = filterSelectionChange(doc, sel, options); } var bias = options && options.bias || (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1); setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)); if (!(options && options.scroll === false) && doc.cm) { ensureCursorVisible(doc.cm); } } function setSelectionInner(doc, sel) { if (sel.equals(doc.sel)) { return } doc.sel = sel; if (doc.cm) { doc.cm.curOp.updateInput = 1; doc.cm.curOp.selectionChanged = true; signalCursorActivity(doc.cm); } signalLater(doc, "cursorActivity", doc); } // Verify that the selection does not partially select any atomic // marked ranges. function reCheckSelection(doc) { setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false)); } // Return a selection that does not partially select any atomic // ranges. function skipAtomicInSelection(doc, sel, bias, mayClear) { var out; for (var i = 0; i < sel.ranges.length; i++) { var range = sel.ranges[i]; var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]; var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear); var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear); if (out || newAnchor != range.anchor || newHead != range.head) { if (!out) { out = sel.ranges.slice(0, i); } out[i] = new Range(newAnchor, newHead); } } return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel } function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { var line = getLine(doc, pos.line); if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { var sp = line.markedSpans[i], m = sp.marker; // Determine if we should prevent the cursor being placed to the left/right of an atomic marker // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it // is with selectLeft/Right var preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft; var preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight; if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) { if (mayClear) { signal(m, "beforeCursorEnter"); if (m.explicitlyCleared) { if (!line.markedSpans) { break } else {--i; continue} } } if (!m.atomic) { continue } if (oldPos) { var near = m.find(dir < 0 ? 1 : -1), diff = (void 0); if (dir < 0 ? preventCursorRight : preventCursorLeft) { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); } if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0)) { return skipAtomicInner(doc, near, pos, dir, mayClear) } } var far = m.find(dir < 0 ? -1 : 1); if (dir < 0 ? preventCursorLeft : preventCursorRight) { far = movePos(doc, far, dir, far.line == pos.line ? line : null); } return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null } } } return pos } // Ensure a given position is not inside an atomic range. function skipAtomic(doc, pos, oldPos, bias, mayClear) { var dir = bias || 1; var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) || (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) || skipAtomicInner(doc, pos, oldPos, -dir, mayClear) || (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true)); if (!found) { doc.cantEdit = true; return Pos(doc.first, 0) } return found } function movePos(doc, pos, dir, line) { if (dir < 0 && pos.ch == 0) { if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) } else { return null } } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) { if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) } else { return null } } else { return new Pos(pos.line, pos.ch + dir) } } function selectAll(cm) { cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll); } // UPDATING // Allow "beforeChange" event handlers to influence a change function filterChange(doc, change, update) { var obj = { canceled: false, from: change.from, to: change.to, text: change.text, origin: change.origin, cancel: function () { return obj.canceled = true; } }; if (update) { obj.update = function (from, to, text, origin) { if (from) { obj.from = clipPos(doc, from); } if (to) { obj.to = clipPos(doc, to); } if (text) { obj.text = text; } if (origin !== undefined) { obj.origin = origin; } }; } signal(doc, "beforeChange", doc, obj); if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); } if (obj.canceled) { if (doc.cm) { doc.cm.curOp.updateInput = 2; } return null } return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin} } // Apply a change to a document, and add it to the document's // history, and propagating it to all linked documents. function makeChange(doc, change, ignoreReadOnly) { if (doc.cm) { if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) } if (doc.cm.state.suppressEdits) { return } } if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { change = filterChange(doc, change, true); if (!change) { return } } // Possibly split or suppress the update based on the presence // of read-only spans in its range. var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to); if (split) { for (var i = split.length - 1; i >= 0; --i) { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); } } else { makeChangeInner(doc, change); } } function makeChangeInner(doc, change) { if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return } var selAfter = computeSelAfterChange(doc, change); addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN); makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)); var rebased = []; linkedDocs(doc, function (doc, sharedHist) { if (!sharedHist && indexOf(rebased, doc.history) == -1) { rebaseHist(doc.history, change); rebased.push(doc.history); } makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)); }); } // Revert a change stored in a document's history. function makeChangeFromHistory(doc, type, allowSelectionOnly) { var suppress = doc.cm && doc.cm.state.suppressEdits; if (suppress && !allowSelectionOnly) { return } var hist = doc.history, event, selAfter = doc.sel; var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done; // Verify that there is a useable event (so that ctrl-z won't // needlessly clear selection events) var i = 0; for (; i < source.length; i++) { event = source[i]; if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) { break } } if (i == source.length) { return } hist.lastOrigin = hist.lastSelOrigin = null; for (;;) { event = source.pop(); if (event.ranges) { pushSelectionToHistory(event, dest); if (allowSelectionOnly && !event.equals(doc.sel)) { setSelection(doc, event, {clearRedo: false}); return } selAfter = event; } else if (suppress) { source.push(event); return } else { break } } // Build up a reverse change object to add to the opposite history // stack (redo when undoing, and vice versa). var antiChanges = []; pushSelectionToHistory(selAfter, dest); dest.push({changes: antiChanges, generation: hist.generation}); hist.generation = event.generation || ++hist.maxGeneration; var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange"); var loop = function ( i ) { var change = event.changes[i]; change.origin = type; if (filter && !filterChange(doc, change, false)) { source.length = 0; return {} } antiChanges.push(historyChangeFromChange(doc, change)); var after = i ? computeSelAfterChange(doc, change) : lst(source); makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); } var rebased = []; // Propagate to the linked documents linkedDocs(doc, function (doc, sharedHist) { if (!sharedHist && indexOf(rebased, doc.history) == -1) { rebaseHist(doc.history, change); rebased.push(doc.history); } makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)); }); }; for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) { var returned = loop( i$1 ); if ( returned ) return returned.v; } } // Sub-views need their line numbers shifted when text is added // above or below them in the parent document. function shiftDoc(doc, distance) { if (distance == 0) { return } doc.first += distance; doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range( Pos(range.anchor.line + distance, range.anchor.ch), Pos(range.head.line + distance, range.head.ch) ); }), doc.sel.primIndex); if (doc.cm) { regChange(doc.cm, doc.first, doc.first - distance, distance); for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) { regLineChange(doc.cm, l, "gutter"); } } } // More lower-level change function, handling only a single document // (not linked ones). function makeChangeSingleDoc(doc, change, selAfter, spans) { if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) } if (change.to.line < doc.first) { shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)); return } if (change.from.line > doc.lastLine()) { return } // Clip the change to the size of this doc if (change.from.line < doc.first) { var shift = change.text.length - 1 - (doc.first - change.from.line); shiftDoc(doc, shift); change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch), text: [lst(change.text)], origin: change.origin}; } var last = doc.lastLine(); if (change.to.line > last) { change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), text: [change.text[0]], origin: change.origin}; } change.removed = getBetween(doc, change.from, change.to); if (!selAfter) { selAfter = computeSelAfterChange(doc, change); } if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); } else { updateDoc(doc, change, spans); } setSelectionNoUndo(doc, selAfter, sel_dontScroll); if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0))) { doc.cantEdit = false; } } // Handle the interaction of a change to a document with the editor // that this document is part of. function makeChangeSingleDocInEditor(cm, change, spans) { var doc = cm.doc, display = cm.display, from = change.from, to = change.to; var recomputeMaxLength = false, checkWidthStart = from.line; if (!cm.options.lineWrapping) { checkWidthStart = lineNo(visualLine(getLine(doc, from.line))); doc.iter(checkWidthStart, to.line + 1, function (line) { if (line == display.maxLine) { recomputeMaxLength = true; return true } }); } if (doc.sel.contains(change.from, change.to) > -1) { signalCursorActivity(cm); } updateDoc(doc, change, spans, estimateHeight(cm)); if (!cm.options.lineWrapping) { doc.iter(checkWidthStart, from.line + change.text.length, function (line) { var len = lineLength(line); if (len > display.maxLineLength) { display.maxLine = line; display.maxLineLength = len; display.maxLineChanged = true; recomputeMaxLength = false; } }); if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; } } retreatFrontier(doc, from.line); startWorker(cm, 400); var lendiff = change.text.length - (to.line - from.line) - 1; // Remember that these lines changed, for updating the display if (change.full) { regChange(cm); } else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) { regLineChange(cm, from.line, "text"); } else { regChange(cm, from.line, to.line + 1, lendiff); } var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change"); if (changeHandler || changesHandler) { var obj = { from: from, to: to, text: change.text, removed: change.removed, origin: change.origin }; if (changeHandler) { signalLater(cm, "change", cm, obj); } if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); } } cm.display.selForContextMenu = null; } function replaceRange(doc, code, from, to, origin) { var assign; if (!to) { to = from; } if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); } if (typeof code == "string") { code = doc.splitLines(code); } makeChange(doc, {from: from, to: to, text: code, origin: origin}); } // Rebasing/resetting history to deal with externally-sourced changes function rebaseHistSelSingle(pos, from, to, diff) { if (to < pos.line) { pos.line += diff; } else if (from < pos.line) { pos.line = from; pos.ch = 0; } } // Tries to rebase an array of history events given a change in the // document. If the change touches the same lines as the event, the // event, and everything 'behind' it, is discarded. If the change is // before the event, the event's positions are updated. Uses a // copy-on-write scheme for the positions, to avoid having to // reallocate them all on every rebase, but also avoid problems with // shared position objects being unsafely updated. function rebaseHistArray(array, from, to, diff) { for (var i = 0; i < array.length; ++i) { var sub = array[i], ok = true; if (sub.ranges) { if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; } for (var j = 0; j < sub.ranges.length; j++) { rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff); rebaseHistSelSingle(sub.ranges[j].head, from, to, diff); } continue } for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) { var cur = sub.changes[j$1]; if (to < cur.from.line) { cur.from = Pos(cur.from.line + diff, cur.from.ch); cur.to = Pos(cur.to.line + diff, cur.to.ch); } else if (from <= cur.to.line) { ok = false; break } } if (!ok) { array.splice(0, i + 1); i = 0; } } } function rebaseHist(hist, change) { var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1; rebaseHistArray(hist.done, from, to, diff); rebaseHistArray(hist.undone, from, to, diff); } // Utility for applying a change to a line by handle or number, // returning the number and optionally registering the line as // changed. function changeLine(doc, handle, changeType, op) { var no = handle, line = handle; if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); } else { no = lineNo(handle); } if (no == null) { return null } if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); } return line } // The document is represented as a BTree consisting of leaves, with // chunk of lines in them, and branches, with up to ten leaves or // other branch nodes below them. The top node is always a branch // node, and is the document object itself (meaning it has // additional methods and properties). // // All nodes have parent links. The tree is used both to go from // line numbers to line objects, and to go from objects to numbers. // It also indexes by height, and is used to convert between height // and line object, and to find the total height of the document. // // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html function LeafChunk(lines) { this.lines = lines; this.parent = null; var height = 0; for (var i = 0; i < lines.length; ++i) { lines[i].parent = this; height += lines[i].height; } this.height = height; } LeafChunk.prototype = { chunkSize: function() { return this.lines.length }, // Remove the n lines at offset 'at'. removeInner: function(at, n) { for (var i = at, e = at + n; i < e; ++i) { var line = this.lines[i]; this.height -= line.height; cleanUpLine(line); signalLater(line, "delete"); } this.lines.splice(at, n); }, // Helper used to collapse a small branch into a single leaf. collapse: function(lines) { lines.push.apply(lines, this.lines); }, // Insert the given array of lines at offset 'at', count them as // having the given height. insertInner: function(at, lines, height) { this.height += height; this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)); for (var i = 0; i < lines.length; ++i) { lines[i].parent = this; } }, // Used to iterate over a part of the tree. iterN: function(at, n, op) { for (var e = at + n; at < e; ++at) { if (op(this.lines[at])) { return true } } } }; function BranchChunk(children) { this.children = children; var size = 0, height = 0; for (var i = 0; i < children.length; ++i) { var ch = children[i]; size += ch.chunkSize(); height += ch.height; ch.parent = this; } this.size = size; this.height = height; this.parent = null; } BranchChunk.prototype = { chunkSize: function() { return this.size }, removeInner: function(at, n) { this.size -= n; for (var i = 0; i < this.children.length; ++i) { var child = this.children[i], sz = child.chunkSize(); if (at < sz) { var rm = Math.min(n, sz - at), oldHeight = child.height; child.removeInner(at, rm); this.height -= oldHeight - child.height; if (sz == rm) { this.children.splice(i--, 1); child.parent = null; } if ((n -= rm) == 0) { break } at = 0; } else { at -= sz; } } // If the result is smaller than 25 lines, ensure that it is a // single leaf node. if (this.size - n < 25 && (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) { var lines = []; this.collapse(lines); this.children = [new LeafChunk(lines)]; this.children[0].parent = this; } }, collapse: function(lines) { for (var i = 0; i < this.children.length; ++i) { this.children[i].collapse(lines); } }, insertInner: function(at, lines, height) { this.size += lines.length; this.height += height; for (var i = 0; i < this.children.length; ++i) { var child = this.children[i], sz = child.chunkSize(); if (at <= sz) { child.insertInner(at, lines, height); if (child.lines && child.lines.length > 50) { // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced. // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest. var remaining = child.lines.length % 25 + 25; for (var pos = remaining; pos < child.lines.length;) { var leaf = new LeafChunk(child.lines.slice(pos, pos += 25)); child.height -= leaf.height; this.children.splice(++i, 0, leaf); leaf.parent = this; } child.lines = child.lines.slice(0, remaining); this.maybeSpill(); } break } at -= sz; } }, // When a node has grown, check whether it should be split. maybeSpill: function() { if (this.children.length <= 10) { return } var me = this; do { var spilled = me.children.splice(me.children.length - 5, 5); var sibling = new BranchChunk(spilled); if (!me.parent) { // Become the parent node var copy = new BranchChunk(me.children); copy.parent = me; me.children = [copy, sibling]; me = copy; } else { me.size -= sibling.size; me.height -= sibling.height; var myIndex = indexOf(me.parent.children, me); me.parent.children.splice(myIndex + 1, 0, sibling); } sibling.parent = me.parent; } while (me.children.length > 10) me.parent.maybeSpill(); }, iterN: function(at, n, op) { for (var i = 0; i < this.children.length; ++i) { var child = this.children[i], sz = child.chunkSize(); if (at < sz) { var used = Math.min(n, sz - at); if (child.iterN(at, used, op)) { return true } if ((n -= used) == 0) { break } at = 0; } else { at -= sz; } } } }; // Line widgets are block elements displayed above or below a line. var LineWidget = function(doc, node, options) { if (options) { for (var opt in options) { if (options.hasOwnProperty(opt)) { this[opt] = options[opt]; } } } this.doc = doc; this.node = node; }; LineWidget.prototype.clear = function () { var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line); if (no == null || !ws) { return } for (var i = 0; i < ws.length; ++i) { if (ws[i] == this) { ws.splice(i--, 1); } } if (!ws.length) { line.widgets = null; } var height = widgetHeight(this); updateLineHeight(line, Math.max(0, line.height - height)); if (cm) { runInOp(cm, function () { adjustScrollWhenAboveVisible(cm, line, -height); regLineChange(cm, no, "widget"); }); signalLater(cm, "lineWidgetCleared", cm, this, no); } }; LineWidget.prototype.changed = function () { var this$1 = this; var oldH = this.height, cm = this.doc.cm, line = this.line; this.height = null; var diff = widgetHeight(this) - oldH; if (!diff) { return } if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); } if (cm) { runInOp(cm, function () { cm.curOp.forceUpdate = true; adjustScrollWhenAboveVisible(cm, line, diff); signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line)); }); } }; eventMixin(LineWidget); function adjustScrollWhenAboveVisible(cm, line, diff) { if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) { addToScrollTop(cm, diff); } } function addLineWidget(doc, handle, node, options) { var widget = new LineWidget(doc, node, options); var cm = doc.cm; if (cm && widget.noHScroll) { cm.display.alignWidgets = true; } changeLine(doc, handle, "widget", function (line) { var widgets = line.widgets || (line.widgets = []); if (widget.insertAt == null) { widgets.push(widget); } else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); } widget.line = line; if (cm && !lineIsHidden(doc, line)) { var aboveVisible = heightAtLine(line) < doc.scrollTop; updateLineHeight(line, line.height + widgetHeight(widget)); if (aboveVisible) { addToScrollTop(cm, widget.height); } cm.curOp.forceUpdate = true; } return true }); if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); } return widget } // TEXTMARKERS // Created with markText and setBookmark methods. A TextMarker is a // handle that can be used to clear or find a marked position in the // document. Line objects hold arrays (markedSpans) containing // {from, to, marker} object pointing to such marker objects, and // indicating that such a marker is present on that line. Multiple // lines may point to the same marker when it spans across lines. // The spans will have null for their from/to properties when the // marker continues beyond the start/end of the line. Markers have // links back to the lines they currently touch. // Collapsed markers have unique ids, in order to be able to order // them, which is needed for uniquely determining an outer marker // when they overlap (they may nest, but not partially overlap). var nextMarkerId = 0; var TextMarker = function(doc, type) { this.lines = []; this.type = type; this.doc = doc; this.id = ++nextMarkerId; }; // Clear the marker. TextMarker.prototype.clear = function () { if (this.explicitlyCleared) { return } var cm = this.doc.cm, withOp = cm && !cm.curOp; if (withOp) { startOperation(cm); } if (hasHandler(this, "clear")) { var found = this.find(); if (found) { signalLater(this, "clear", found.from, found.to); } } var min = null, max = null; for (var i = 0; i < this.lines.length; ++i) { var line = this.lines[i]; var span = getMarkedSpanFor(line.markedSpans, this); if (cm && !this.collapsed) { regLineChange(cm, lineNo(line), "text"); } else if (cm) { if (span.to != null) { max = lineNo(line); } if (span.from != null) { min = lineNo(line); } } line.markedSpans = removeMarkedSpan(line.markedSpans, span); if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm) { updateLineHeight(line, textHeight(cm.display)); } } if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) { var visual = visualLine(this.lines[i$1]), len = lineLength(visual); if (len > cm.display.maxLineLength) { cm.display.maxLine = visual; cm.display.maxLineLength = len; cm.display.maxLineChanged = true; } } } if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); } this.lines.length = 0; this.explicitlyCleared = true; if (this.atomic && this.doc.cantEdit) { this.doc.cantEdit = false; if (cm) { reCheckSelection(cm.doc); } } if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); } if (withOp) { endOperation(cm); } if (this.parent) { this.parent.clear(); } }; // Find the position of the marker in the document. Returns a {from, // to} object by default. Side can be passed to get a specific side // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the // Pos objects returned contain a line object, rather than a line // number (used to prevent looking up the same line twice). TextMarker.prototype.find = function (side, lineObj) { if (side == null && this.type == "bookmark") { side = 1; } var from, to; for (var i = 0; i < this.lines.length; ++i) { var line = this.lines[i]; var span = getMarkedSpanFor(line.markedSpans, this); if (span.from != null) { from = Pos(lineObj ? line : lineNo(line), span.from); if (side == -1) { return from } } if (span.to != null) { to = Pos(lineObj ? line : lineNo(line), span.to); if (side == 1) { return to } } } return from && {from: from, to: to} }; // Signals that the marker's widget changed, and surrounding layout // should be recomputed. TextMarker.prototype.changed = function () { var this$1 = this; var pos = this.find(-1, true), widget = this, cm = this.doc.cm; if (!pos || !cm) { return } runInOp(cm, function () { var line = pos.line, lineN = lineNo(pos.line); var view = findViewForLine(cm, lineN); if (view) { clearLineMeasurementCacheFor(view); cm.curOp.selectionChanged = cm.curOp.forceUpdate = true; } cm.curOp.updateMaxLine = true; if (!lineIsHidden(widget.doc, line) && widget.height != null) { var oldHeight = widget.height; widget.height = null; var dHeight = widgetHeight(widget) - oldHeight; if (dHeight) { updateLineHeight(line, line.height + dHeight); } } signalLater(cm, "markerChanged", cm, this$1); }); }; TextMarker.prototype.attachLine = function (line) { if (!this.lines.length && this.doc.cm) { var op = this.doc.cm.curOp; if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); } } this.lines.push(line); }; TextMarker.prototype.detachLine = function (line) { this.lines.splice(indexOf(this.lines, line), 1); if (!this.lines.length && this.doc.cm) { var op = this.doc.cm.curOp ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this); } }; eventMixin(TextMarker); // Create a marker, wire it up to the right lines, and function markText(doc, from, to, options, type) { // Shared markers (across linked documents) are handled separately // (markTextShared will call out to this again, once per // document). if (options && options.shared) { return markTextShared(doc, from, to, options, type) } // Ensure we are in an operation. if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) } var marker = new TextMarker(doc, type), diff = cmp(from, to); if (options) { copyObj(options, marker, false); } // Don't connect empty markers unless clearWhenEmpty is false if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) { return marker } if (marker.replacedWith) { // Showing up as a widget implies collapsed (widget replaces text) marker.collapsed = true; marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget"); if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); } if (options.insertLeft) { marker.widgetNode.insertLeft = true; } } if (marker.collapsed) { if (conflictingCollapsedRange(doc, from.line, from, to, marker) || from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker)) { throw new Error("Inserting collapsed marker partially overlapping an existing one") } seeCollapsedSpans(); } if (marker.addToHistory) { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); } var curLine = from.line, cm = doc.cm, updateMaxLine; doc.iter(curLine, to.line + 1, function (line) { if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) { updateMaxLine = true; } if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); } addMarkedSpan(line, new MarkedSpan(marker, curLine == from.line ? from.ch : null, curLine == to.line ? to.ch : null)); ++curLine; }); // lineIsHidden depends on the presence of the spans, so needs a second pass if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) { if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); } }); } if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); } if (marker.readOnly) { seeReadOnlySpans(); if (doc.history.done.length || doc.history.undone.length) { doc.clearHistory(); } } if (marker.collapsed) { marker.id = ++nextMarkerId; marker.atomic = true; } if (cm) { // Sync editor state if (updateMaxLine) { cm.curOp.updateMaxLine = true; } if (marker.collapsed) { regChange(cm, from.line, to.line + 1); } else if (marker.className || marker.startStyle || marker.endStyle || marker.css || marker.attributes || marker.title) { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } } if (marker.atomic) { reCheckSelection(cm.doc); } signalLater(cm, "markerAdded", cm, marker); } return marker } // SHARED TEXTMARKERS // A shared marker spans multiple linked documents. It is // implemented as a meta-marker-object controlling multiple normal // markers. var SharedTextMarker = function(markers, primary) { this.markers = markers; this.primary = primary; for (var i = 0; i < markers.length; ++i) { markers[i].parent = this; } }; SharedTextMarker.prototype.clear = function () { if (this.explicitlyCleared) { return } this.explicitlyCleared = true; for (var i = 0; i < this.markers.length; ++i) { this.markers[i].clear(); } signalLater(this, "clear"); }; SharedTextMarker.prototype.find = function (side, lineObj) { return this.primary.find(side, lineObj) }; eventMixin(SharedTextMarker); function markTextShared(doc, from, to, options, type) { options = copyObj(options); options.shared = false; var markers = [markText(doc, from, to, options, type)], primary = markers[0]; var widget = options.widgetNode; linkedDocs(doc, function (doc) { if (widget) { options.widgetNode = widget.cloneNode(true); } markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)); for (var i = 0; i < doc.linked.length; ++i) { if (doc.linked[i].isParent) { return } } primary = lst(markers); }); return new SharedTextMarker(markers, primary) } function findSharedMarkers(doc) { return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; }) } function copySharedMarkers(doc, markers) { for (var i = 0; i < markers.length; i++) { var marker = markers[i], pos = marker.find(); var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to); if (cmp(mFrom, mTo)) { var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type); marker.markers.push(subMark); subMark.parent = marker; } } } function detachSharedMarkers(markers) { var loop = function ( i ) { var marker = markers[i], linked = [marker.primary.doc]; linkedDocs(marker.primary.doc, function (d) { return linked.push(d); }); for (var j = 0; j < marker.markers.length; j++) { var subMarker = marker.markers[j]; if (indexOf(linked, subMarker.doc) == -1) { subMarker.parent = null; marker.markers.splice(j--, 1); } } }; for (var i = 0; i < markers.length; i++) loop( i ); } var nextDocId = 0; var Doc = function(text, mode, firstLine, lineSep, direction) { if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) } if (firstLine == null) { firstLine = 0; } BranchChunk.call(this, [new LeafChunk([new Line("", null)])]); this.first = firstLine; this.scrollTop = this.scrollLeft = 0; this.cantEdit = false; this.cleanGeneration = 1; this.modeFrontier = this.highlightFrontier = firstLine; var start = Pos(firstLine, 0); this.sel = simpleSelection(start); this.history = new History(null); this.id = ++nextDocId; this.modeOption = mode; this.lineSep = lineSep; this.direction = (direction == "rtl") ? "rtl" : "ltr"; this.extend = false; if (typeof text == "string") { text = this.splitLines(text); } updateDoc(this, {from: start, to: start, text: text}); setSelection(this, simpleSelection(start), sel_dontScroll); }; Doc.prototype = createObj(BranchChunk.prototype, { constructor: Doc, // Iterate over the document. Supports two forms -- with only one // argument, it calls that for each line in the document. With // three, it iterates over the range given by the first two (with // the second being non-inclusive). iter: function(from, to, op) { if (op) { this.iterN(from - this.first, to - from, op); } else { this.iterN(this.first, this.first + this.size, from); } }, // Non-public interface for adding and removing lines. insert: function(at, lines) { var height = 0; for (var i = 0; i < lines.length; ++i) { height += lines[i].height; } this.insertInner(at - this.first, lines, height); }, remove: function(at, n) { this.removeInner(at - this.first, n); }, // From here, the methods are part of the public interface. Most // are also available from CodeMirror (editor) instances. getValue: function(lineSep) { var lines = getLines(this, this.first, this.first + this.size); if (lineSep === false) { return lines } return lines.join(lineSep || this.lineSeparator()) }, setValue: docMethodOp(function(code) { var top = Pos(this.first, 0), last = this.first + this.size - 1; makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), text: this.splitLines(code), origin: "setValue", full: true}, true); if (this.cm) { scrollToCoords(this.cm, 0, 0); } setSelection(this, simpleSelection(top), sel_dontScroll); }), replaceRange: function(code, from, to, origin) { from = clipPos(this, from); to = to ? clipPos(this, to) : from; replaceRange(this, code, from, to, origin); }, getRange: function(from, to, lineSep) { var lines = getBetween(this, clipPos(this, from), clipPos(this, to)); if (lineSep === false) { return lines } return lines.join(lineSep || this.lineSeparator()) }, getLine: function(line) {var l = this.getLineHandle(line); return l && l.text}, getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }}, getLineNumber: function(line) {return lineNo(line)}, getLineHandleVisualStart: function(line) { if (typeof line == "number") { line = getLine(this, line); } return visualLine(line) }, lineCount: function() {return this.size}, firstLine: function() {return this.first}, lastLine: function() {return this.first + this.size - 1}, clipPos: function(pos) {return clipPos(this, pos)}, getCursor: function(start) { var range = this.sel.primary(), pos; if (start == null || start == "head") { pos = range.head; } else if (start == "anchor") { pos = range.anchor; } else if (start == "end" || start == "to" || start === false) { pos = range.to(); } else { pos = range.from(); } return pos }, listSelections: function() { return this.sel.ranges }, somethingSelected: function() {return this.sel.somethingSelected()}, setCursor: docMethodOp(function(line, ch, options) { setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options); }), setSelection: docMethodOp(function(anchor, head, options) { setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options); }), extendSelection: docMethodOp(function(head, other, options) { extendSelection(this, clipPos(this, head), other && clipPos(this, other), options); }), extendSelections: docMethodOp(function(heads, options) { extendSelections(this, clipPosArray(this, heads), options); }), extendSelectionsBy: docMethodOp(function(f, options) { var heads = map(this.sel.ranges, f); extendSelections(this, clipPosArray(this, heads), options); }), setSelections: docMethodOp(function(ranges, primary, options) { if (!ranges.length) { return } var out = []; for (var i = 0; i < ranges.length; i++) { out[i] = new Range(clipPos(this, ranges[i].anchor), clipPos(this, ranges[i].head)); } if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); } setSelection(this, normalizeSelection(this.cm, out, primary), options); }), addSelection: docMethodOp(function(anchor, head, options) { var ranges = this.sel.ranges.slice(0); ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))); setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options); }), getSelection: function(lineSep) { var ranges = this.sel.ranges, lines; for (var i = 0; i < ranges.length; i++) { var sel = getBetween(this, ranges[i].from(), ranges[i].to()); lines = lines ? lines.concat(sel) : sel; } if (lineSep === false) { return lines } else { return lines.join(lineSep || this.lineSeparator()) } }, getSelections: function(lineSep) { var parts = [], ranges = this.sel.ranges; for (var i = 0; i < ranges.length; i++) { var sel = getBetween(this, ranges[i].from(), ranges[i].to()); if (lineSep !== false) { sel = sel.join(lineSep || this.lineSeparator()); } parts[i] = sel; } return parts }, replaceSelection: function(code, collapse, origin) { var dup = []; for (var i = 0; i < this.sel.ranges.length; i++) { dup[i] = code; } this.replaceSelections(dup, collapse, origin || "+input"); }, replaceSelections: docMethodOp(function(code, collapse, origin) { var changes = [], sel = this.sel; for (var i = 0; i < sel.ranges.length; i++) { var range = sel.ranges[i]; changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin}; } var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse); for (var i$1 = changes.length - 1; i$1 >= 0; i$1--) { makeChange(this, changes[i$1]); } if (newSel) { setSelectionReplaceHistory(this, newSel); } else if (this.cm) { ensureCursorVisible(this.cm); } }), undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}), redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}), undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}), redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}), setExtending: function(val) {this.extend = val;}, getExtending: function() {return this.extend}, historySize: function() { var hist = this.history, done = 0, undone = 0; for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } } for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } } return {undo: done, redo: undone} }, clearHistory: function() { var this$1 = this; this.history = new History(this.history.maxGeneration); linkedDocs(this, function (doc) { return doc.history = this$1.history; }, true); }, markClean: function() { this.cleanGeneration = this.changeGeneration(true); }, changeGeneration: function(forceSplit) { if (forceSplit) { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; } return this.history.generation }, isClean: function (gen) { return this.history.generation == (gen || this.cleanGeneration) }, getHistory: function() { return {done: copyHistoryArray(this.history.done), undone: copyHistoryArray(this.history.undone)} }, setHistory: function(histData) { var hist = this.history = new History(this.history.maxGeneration); hist.done = copyHistoryArray(histData.done.slice(0), null, true); hist.undone = copyHistoryArray(histData.undone.slice(0), null, true); }, setGutterMarker: docMethodOp(function(line, gutterID, value) { return changeLine(this, line, "gutter", function (line) { var markers = line.gutterMarkers || (line.gutterMarkers = {}); markers[gutterID] = value; if (!value && isEmpty(markers)) { line.gutterMarkers = null; } return true }) }), clearGutter: docMethodOp(function(gutterID) { var this$1 = this; this.iter(function (line) { if (line.gutterMarkers && line.gutterMarkers[gutterID]) { changeLine(this$1, line, "gutter", function () { line.gutterMarkers[gutterID] = null; if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; } return true }); } }); }), lineInfo: function(line) { var n; if (typeof line == "number") { if (!isLine(this, line)) { return null } n = line; line = getLine(this, line); if (!line) { return null } } else { n = lineNo(line); if (n == null) { return null } } return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, widgets: line.widgets} }, addLineClass: docMethodOp(function(handle, where, cls) { return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : where == "gutter" ? "gutterClass" : "wrapClass"; if (!line[prop]) { line[prop] = cls; } else if (classTest(cls).test(line[prop])) { return false } else { line[prop] += " " + cls; } return true }) }), removeLineClass: docMethodOp(function(handle, where, cls) { return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : where == "gutter" ? "gutterClass" : "wrapClass"; var cur = line[prop]; if (!cur) { return false } else if (cls == null) { line[prop] = null; } else { var found = cur.match(classTest(cls)); if (!found) { return false } var end = found.index + found[0].length; line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null; } return true }) }), addLineWidget: docMethodOp(function(handle, node, options) { return addLineWidget(this, handle, node, options) }), removeLineWidget: function(widget) { widget.clear(); }, markText: function(from, to, options) { return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range") }, setBookmark: function(pos, options) { var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), insertLeft: options && options.insertLeft, clearWhenEmpty: false, shared: options && options.shared, handleMouseEvents: options && options.handleMouseEvents}; pos = clipPos(this, pos); return markText(this, pos, pos, realOpts, "bookmark") }, findMarksAt: function(pos) { pos = clipPos(this, pos); var markers = [], spans = getLine(this, pos.line).markedSpans; if (spans) { for (var i = 0; i < spans.length; ++i) { var span = spans[i]; if ((span.from == null || span.from <= pos.ch) && (span.to == null || span.to >= pos.ch)) { markers.push(span.marker.parent || span.marker); } } } return markers }, findMarks: function(from, to, filter) { from = clipPos(this, from); to = clipPos(this, to); var found = [], lineNo = from.line; this.iter(from.line, to.line + 1, function (line) { var spans = line.markedSpans; if (spans) { for (var i = 0; i < spans.length; i++) { var span = spans[i]; if (!(span.to != null && lineNo == from.line && from.ch >= span.to || span.from == null && lineNo != from.line || span.from != null && lineNo == to.line && span.from >= to.ch) && (!filter || filter(span.marker))) { found.push(span.marker.parent || span.marker); } } } ++lineNo; }); return found }, getAllMarks: function() { var markers = []; this.iter(function (line) { var sps = line.markedSpans; if (sps) { for (var i = 0; i < sps.length; ++i) { if (sps[i].from != null) { markers.push(sps[i].marker); } } } }); return markers }, posFromIndex: function(off) { var ch, lineNo = this.first, sepSize = this.lineSeparator().length; this.iter(function (line) { var sz = line.text.length + sepSize; if (sz > off) { ch = off; return true } off -= sz; ++lineNo; }); return clipPos(this, Pos(lineNo, ch)) }, indexFromPos: function (coords) { coords = clipPos(this, coords); var index = coords.ch; if (coords.line < this.first || coords.ch < 0) { return 0 } var sepSize = this.lineSeparator().length; this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value index += line.text.length + sepSize; }); return index }, copy: function(copyHistory) { var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first, this.lineSep, this.direction); doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft; doc.sel = this.sel; doc.extend = false; if (copyHistory) { doc.history.undoDepth = this.history.undoDepth; doc.setHistory(this.getHistory()); } return doc }, linkedDoc: function(options) { if (!options) { options = {}; } var from = this.first, to = this.first + this.size; if (options.from != null && options.from > from) { from = options.from; } if (options.to != null && options.to < to) { to = options.to; } var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction); if (options.sharedHist) { copy.history = this.history ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist}); copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]; copySharedMarkers(copy, findSharedMarkers(this)); return copy }, unlinkDoc: function(other) { if (other instanceof CodeMirror) { other = other.doc; } if (this.linked) { for (var i = 0; i < this.linked.length; ++i) { var link = this.linked[i]; if (link.doc != other) { continue } this.linked.splice(i, 1); other.unlinkDoc(this); detachSharedMarkers(findSharedMarkers(this)); break } } // If the histories were shared, split them again if (other.history == this.history) { var splitIds = [other.id]; linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true); other.history = new History(null); other.history.done = copyHistoryArray(this.history.done, splitIds); other.history.undone = copyHistoryArray(this.history.undone, splitIds); } }, iterLinkedDocs: function(f) {linkedDocs(this, f);}, getMode: function() {return this.mode}, getEditor: function() {return this.cm}, splitLines: function(str) { if (this.lineSep) { return str.split(this.lineSep) } return splitLinesAuto(str) }, lineSeparator: function() { return this.lineSep || "\n" }, setDirection: docMethodOp(function (dir) { if (dir != "rtl") { dir = "ltr"; } if (dir == this.direction) { return } this.direction = dir; this.iter(function (line) { return line.order = null; }); if (this.cm) { directionChanged(this.cm); } }) }); // Public alias. Doc.prototype.eachLine = Doc.prototype.iter; // Kludge to work around strange IE behavior where it'll sometimes // re-fire a series of drag-related events right after the drop (#1551) var lastDrop = 0; function onDrop(e) { var cm = this; clearDragCursor(cm); if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return } e_preventDefault(e); if (ie) { lastDrop = +new Date; } var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files; if (!pos || cm.isReadOnly()) { return } // Might be a file drop, in which case we simply extract the text // and insert it. if (files && files.length && window.FileReader && window.File) { var n = files.length, text = Array(n), read = 0; var markAsReadAndPasteIfAllFilesAreRead = function () { if (++read == n) { operation(cm, function () { pos = clipPos(cm.doc, pos); var change = {from: pos, to: pos, text: cm.doc.splitLines( text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())), origin: "paste"}; makeChange(cm.doc, change); setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change))); })(); } }; var readTextFromFile = function (file, i) { if (cm.options.allowDropFileTypes && indexOf(cm.options.allowDropFileTypes, file.type) == -1) { markAsReadAndPasteIfAllFilesAreRead(); return } var reader = new FileReader; reader.onerror = function () { return markAsReadAndPasteIfAllFilesAreRead(); }; reader.onload = function () { var content = reader.result; if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { markAsReadAndPasteIfAllFilesAreRead(); return } text[i] = content; markAsReadAndPasteIfAllFilesAreRead(); }; reader.readAsText(file); }; for (var i = 0; i < files.length; i++) { readTextFromFile(files[i], i); } } else { // Normal drop // Don't do a replace if the drop happened inside of the selected text. if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { cm.state.draggingText(e); // Ensure the editor is re-focused setTimeout(function () { return cm.display.input.focus(); }, 20); return } try { var text$1 = e.dataTransfer.getData("Text"); if (text$1) { var selected; if (cm.state.draggingText && !cm.state.draggingText.copy) { selected = cm.listSelections(); } setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)); if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1) { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } } cm.replaceSelection(text$1, "around", "paste"); cm.display.input.focus(); } } catch(e){} } } function onDragStart(cm, e) { if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return } if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return } e.dataTransfer.setData("Text", cm.getSelection()); e.dataTransfer.effectAllowed = "copyMove"; // Use dummy image instead of default browsers image. // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. if (e.dataTransfer.setDragImage && !safari) { var img = elt("img", null, null, "position: fixed; left: 0; top: 0;"); img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; if (presto) { img.width = img.height = 1; cm.display.wrapper.appendChild(img); // Force a relayout, or Opera won't use our image for some obscure reason img._top = img.offsetTop; } e.dataTransfer.setDragImage(img, 0, 0); if (presto) { img.parentNode.removeChild(img); } } } function onDragOver(cm, e) { var pos = posFromMouse(cm, e); if (!pos) { return } var frag = document.createDocumentFragment(); drawSelectionCursor(cm, pos, frag); if (!cm.display.dragCursor) { cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors"); cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv); } removeChildrenAndAdd(cm.display.dragCursor, frag); } function clearDragCursor(cm) { if (cm.display.dragCursor) { cm.display.lineSpace.removeChild(cm.display.dragCursor); cm.display.dragCursor = null; } } // These must be handled carefully, because naively registering a // handler for each editor will cause the editors to never be // garbage collected. function forEachCodeMirror(f) { if (!document.getElementsByClassName) { return } var byClass = document.getElementsByClassName("CodeMirror"), editors = []; for (var i = 0; i < byClass.length; i++) { var cm = byClass[i].CodeMirror; if (cm) { editors.push(cm); } } if (editors.length) { editors[0].operation(function () { for (var i = 0; i < editors.length; i++) { f(editors[i]); } }); } } var globalsRegistered = false; function ensureGlobalHandlers() { if (globalsRegistered) { return } registerGlobalHandlers(); globalsRegistered = true; } function registerGlobalHandlers() { // When the window resizes, we need to refresh active editors. var resizeTimer; on(window, "resize", function () { if (resizeTimer == null) { resizeTimer = setTimeout(function () { resizeTimer = null; forEachCodeMirror(onResize); }, 100); } }); // When the window loses focus, we want to show the editor as blurred on(window, "blur", function () { return forEachCodeMirror(onBlur); }); } // Called when the window resizes function onResize(cm) { var d = cm.display; // Might be a text scaling operation, clear size caches. d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; d.scrollbarsClipped = false; cm.setSize(); } var keyNames = { 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock", 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" }; // Number keys for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); } // Alphabetic keys for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); } // Function keys for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; } var keyMap = {}; keyMap.basic = { "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto", "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", "Esc": "singleSelection" }; // Note that the save and find-related commands aren't defined by // default. User code or addons can define them. Unknown commands // are simply ignored. keyMap.pcDefault = { "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown", "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", "fallthrough": "basic" }; // Very basic readline/emacs-style bindings, which are standard on Mac. keyMap.emacsy = { "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars", "Ctrl-O": "openLine" }; keyMap.macDefault = { "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore", "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", "fallthrough": ["basic", "emacsy"] }; keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; // KEYMAP DISPATCH function normalizeKeyName(name) { var parts = name.split(/-(?!$)/); name = parts[parts.length - 1]; var alt, ctrl, shift, cmd; for (var i = 0; i < parts.length - 1; i++) { var mod = parts[i]; if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; } else if (/^a(lt)?$/i.test(mod)) { alt = true; } else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; } else if (/^s(hift)?$/i.test(mod)) { shift = true; } else { throw new Error("Unrecognized modifier name: " + mod) } } if (alt) { name = "Alt-" + name; } if (ctrl) { name = "Ctrl-" + name; } if (cmd) { name = "Cmd-" + name; } if (shift) { name = "Shift-" + name; } return name } // This is a kludge to keep keymaps mostly working as raw objects // (backwards compatibility) while at the same time support features // like normalization and multi-stroke key bindings. It compiles a // new normalized keymap, and then updates the old object to reflect // this. function normalizeKeyMap(keymap) { var copy = {}; for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) { var value = keymap[keyname]; if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue } if (value == "...") { delete keymap[keyname]; continue } var keys = map(keyname.split(" "), normalizeKeyName); for (var i = 0; i < keys.length; i++) { var val = (void 0), name = (void 0); if (i == keys.length - 1) { name = keys.join(" "); val = value; } else { name = keys.slice(0, i + 1).join(" "); val = "..."; } var prev = copy[name]; if (!prev) { copy[name] = val; } else if (prev != val) { throw new Error("Inconsistent bindings for " + name) } } delete keymap[keyname]; } } for (var prop in copy) { keymap[prop] = copy[prop]; } return keymap } function lookupKey(key, map, handle, context) { map = getKeyMap(map); var found = map.call ? map.call(key, context) : map[key]; if (found === false) { return "nothing" } if (found === "...") { return "multi" } if (found != null && handle(found)) { return "handled" } if (map.fallthrough) { if (Object.prototype.toString.call(map.fallthrough) != "[object Array]") { return lookupKey(key, map.fallthrough, handle, context) } for (var i = 0; i < map.fallthrough.length; i++) { var result = lookupKey(key, map.fallthrough[i], handle, context); if (result) { return result } } } } // Modifier key presses don't count as 'real' key presses for the // purpose of keymap fallthrough. function isModifierKey(value) { var name = typeof value == "string" ? value : keyNames[value.keyCode]; return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod" } function addModifierNames(name, event, noShift) { var base = name; if (event.altKey && base != "Alt") { name = "Alt-" + name; } if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; } if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; } if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; } return name } // Look up the name of a key as indicated by an event object. function keyName(event, noShift) { if (presto && event.keyCode == 34 && event["char"]) { return false } var name = keyNames[event.keyCode]; if (name == null || event.altGraphKey) { return false } // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause, // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+) if (event.keyCode == 3 && event.code) { name = event.code; } return addModifierNames(name, event, noShift) } function getKeyMap(val) { return typeof val == "string" ? keyMap[val] : val } // Helper for deleting text near the selection(s), used to implement // backspace, delete, and similar functionality. function deleteNearSelection(cm, compute) { var ranges = cm.doc.sel.ranges, kill = []; // Build up a set of ranges to kill first, merging overlapping // ranges. for (var i = 0; i < ranges.length; i++) { var toKill = compute(ranges[i]); while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { var replaced = kill.pop(); if (cmp(replaced.from, toKill.from) < 0) { toKill.from = replaced.from; break } } kill.push(toKill); } // Next, remove those actual ranges. runInOp(cm, function () { for (var i = kill.length - 1; i >= 0; i--) { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); } ensureCursorVisible(cm); }); } function moveCharLogically(line, ch, dir) { var target = skipExtendingChars(line.text, ch + dir, dir); return target < 0 || target > line.text.length ? null : target } function moveLogically(line, start, dir) { var ch = moveCharLogically(line, start.ch, dir); return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before") } function endOfLine(visually, cm, lineObj, lineNo, dir) { if (visually) { if (cm.getOption("direction") == "rtl") { dir = -dir; } var order = getOrder(lineObj, cm.doc.direction); if (order) { var part = dir < 0 ? lst(order) : order[0]; var moveInStorageOrder = (dir < 0) == (part.level == 1); var sticky = moveInStorageOrder ? "after" : "before"; var ch; // With a wrapped rtl chunk (possibly spanning multiple bidi parts), // it could be that the last bidi part is not on the last visual line, // since visual lines contain content order-consecutive chunks. // Thus, in rtl, we are looking for the first (content-order) character // in the rtl chunk that is on the last line (that is, the same line // as the last (content-order) character). if (part.level > 0 || cm.doc.direction == "rtl") { var prep = prepareMeasureForLine(cm, lineObj); ch = dir < 0 ? lineObj.text.length - 1 : 0; var targetTop = measureCharPrepared(cm, prep, ch).top; ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch); if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); } } else { ch = dir < 0 ? part.to : part.from; } return new Pos(lineNo, ch, sticky) } } return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after") } function moveVisually(cm, line, start, dir) { var bidi = getOrder(line, cm.doc.direction); if (!bidi) { return moveLogically(line, start, dir) } if (start.ch >= line.text.length) { start.ch = line.text.length; start.sticky = "before"; } else if (start.ch <= 0) { start.ch = 0; start.sticky = "after"; } var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]; if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) { // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines, // nothing interesting happens. return moveLogically(line, start, dir) } var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }; var prep; var getWrappedLineExtent = function (ch) { if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} } prep = prep || prepareMeasureForLine(cm, line); return wrappedLineExtentChar(cm, line, prep, ch) }; var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch); if (cm.doc.direction == "rtl" || part.level == 1) { var moveInStorageOrder = (part.level == 1) == (dir < 0); var ch = mv(start, moveInStorageOrder ? 1 : -1); if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) { // Case 2: We move within an rtl part or in an rtl editor on the same visual line var sticky = moveInStorageOrder ? "before" : "after"; return new Pos(start.line, ch, sticky) } } // Case 3: Could not move within this bidi part in this visual line, so leave // the current bidi part var searchInVisualLine = function (partPos, dir, wrappedLineExtent) { var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder ? new Pos(start.line, mv(ch, 1), "before") : new Pos(start.line, ch, "after"); }; for (; partPos >= 0 && partPos < bidi.length; partPos += dir) { var part = bidi[partPos]; var moveInStorageOrder = (dir > 0) == (part.level != 1); var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1); if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) } ch = moveInStorageOrder ? part.from : mv(part.to, -1); if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) } } }; // Case 3a: Look for other bidi parts on the same visual line var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent); if (res) { return res } // Case 3b: Look for other bidi parts on the next visual line var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1); if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) { res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh)); if (res) { return res } } // Case 4: Nowhere to move return null } // Commands are parameter-less actions that can be performed on an // editor, mostly used for keybindings. var commands = { selectAll: selectAll, singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); }, killLine: function (cm) { return deleteNearSelection(cm, function (range) { if (range.empty()) { var len = getLine(cm.doc, range.head.line).text.length; if (range.head.ch == len && range.head.line < cm.lastLine()) { return {from: range.head, to: Pos(range.head.line + 1, 0)} } else { return {from: range.head, to: Pos(range.head.line, len)} } } else { return {from: range.from(), to: range.to()} } }); }, deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({ from: Pos(range.from().line, 0), to: clipPos(cm.doc, Pos(range.to().line + 1, 0)) }); }); }, delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({ from: Pos(range.from().line, 0), to: range.from() }); }); }, delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { var top = cm.charCoords(range.head, "div").top + 5; var leftPos = cm.coordsChar({left: 0, top: top}, "div"); return {from: leftPos, to: range.from()} }); }, delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) { var top = cm.charCoords(range.head, "div").top + 5; var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"); return {from: range.from(), to: rightPos } }); }, undo: function (cm) { return cm.undo(); }, redo: function (cm) { return cm.redo(); }, undoSelection: function (cm) { return cm.undoSelection(); }, redoSelection: function (cm) { return cm.redoSelection(); }, goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); }, goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); }, goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); }, {origin: "+move", bias: 1} ); }, goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); }, {origin: "+move", bias: 1} ); }, goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); }, {origin: "+move", bias: -1} ); }, goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) { var top = cm.cursorCoords(range.head, "div").top + 5; return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") }, sel_move); }, goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) { var top = cm.cursorCoords(range.head, "div").top + 5; return cm.coordsChar({left: 0, top: top}, "div") }, sel_move); }, goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) { var top = cm.cursorCoords(range.head, "div").top + 5; var pos = cm.coordsChar({left: 0, top: top}, "div"); if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) } return pos }, sel_move); }, goLineUp: function (cm) { return cm.moveV(-1, "line"); }, goLineDown: function (cm) { return cm.moveV(1, "line"); }, goPageUp: function (cm) { return cm.moveV(-1, "page"); }, goPageDown: function (cm) { return cm.moveV(1, "page"); }, goCharLeft: function (cm) { return cm.moveH(-1, "char"); }, goCharRight: function (cm) { return cm.moveH(1, "char"); }, goColumnLeft: function (cm) { return cm.moveH(-1, "column"); }, goColumnRight: function (cm) { return cm.moveH(1, "column"); }, goWordLeft: function (cm) { return cm.moveH(-1, "word"); }, goGroupRight: function (cm) { return cm.moveH(1, "group"); }, goGroupLeft: function (cm) { return cm.moveH(-1, "group"); }, goWordRight: function (cm) { return cm.moveH(1, "word"); }, delCharBefore: function (cm) { return cm.deleteH(-1, "char"); }, delCharAfter: function (cm) { return cm.deleteH(1, "char"); }, delWordBefore: function (cm) { return cm.deleteH(-1, "word"); }, delWordAfter: function (cm) { return cm.deleteH(1, "word"); }, delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); }, delGroupAfter: function (cm) { return cm.deleteH(1, "group"); }, indentAuto: function (cm) { return cm.indentSelection("smart"); }, indentMore: function (cm) { return cm.indentSelection("add"); }, indentLess: function (cm) { return cm.indentSelection("subtract"); }, insertTab: function (cm) { return cm.replaceSelection("\t"); }, insertSoftTab: function (cm) { var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize; for (var i = 0; i < ranges.length; i++) { var pos = ranges[i].from(); var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize); spaces.push(spaceStr(tabSize - col % tabSize)); } cm.replaceSelections(spaces); }, defaultTab: function (cm) { if (cm.somethingSelected()) { cm.indentSelection("add"); } else { cm.execCommand("insertTab"); } }, // Swap the two chars left and right of each selection's head. // Move cursor behind the two swapped characters afterwards. // // Doesn't consider line feeds a character. // Doesn't scan more than one line above to find a character. // Doesn't do anything on an empty line. // Doesn't do anything with non-empty selections. transposeChars: function (cm) { return runInOp(cm, function () { var ranges = cm.listSelections(), newSel = []; for (var i = 0; i < ranges.length; i++) { if (!ranges[i].empty()) { continue } var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text; if (line) { if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); } if (cur.ch > 0) { cur = new Pos(cur.line, cur.ch + 1); cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), Pos(cur.line, cur.ch - 2), cur, "+transpose"); } else if (cur.line > cm.doc.first) { var prev = getLine(cm.doc, cur.line - 1).text; if (prev) { cur = new Pos(cur.line, 1); cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + prev.charAt(prev.length - 1), Pos(cur.line - 1, prev.length - 1), cur, "+transpose"); } } } newSel.push(new Range(cur, cur)); } cm.setSelections(newSel); }); }, newlineAndIndent: function (cm) { return runInOp(cm, function () { var sels = cm.listSelections(); for (var i = sels.length - 1; i >= 0; i--) { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); } sels = cm.listSelections(); for (var i$1 = 0; i$1 < sels.length; i$1++) { cm.indentLine(sels[i$1].from().line, null, true); } ensureCursorVisible(cm); }); }, openLine: function (cm) { return cm.replaceSelection("\n", "start"); }, toggleOverwrite: function (cm) { return cm.toggleOverwrite(); } }; function lineStart(cm, lineN) { var line = getLine(cm.doc, lineN); var visual = visualLine(line); if (visual != line) { lineN = lineNo(visual); } return endOfLine(true, cm, visual, lineN, 1) } function lineEnd(cm, lineN) { var line = getLine(cm.doc, lineN); var visual = visualLineEnd(line); if (visual != line) { lineN = lineNo(visual); } return endOfLine(true, cm, line, lineN, -1) } function lineStartSmart(cm, pos) { var start = lineStart(cm, pos.line); var line = getLine(cm.doc, start.line); var order = getOrder(line, cm.doc.direction); if (!order || order[0].level == 0) { var firstNonWS = Math.max(0, line.text.search(/\S/)); var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch; return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky) } return start } // Run a handler that was bound to a key. function doHandleBinding(cm, bound, dropShift) { if (typeof bound == "string") { bound = commands[bound]; if (!bound) { return false } } // Ensure previous input has been read, so that the handler sees a // consistent view of the document cm.display.input.ensurePolled(); var prevShift = cm.display.shift, done = false; try { if (cm.isReadOnly()) { cm.state.suppressEdits = true; } if (dropShift) { cm.display.shift = false; } done = bound(cm) != Pass; } finally { cm.display.shift = prevShift; cm.state.suppressEdits = false; } return done } function lookupKeyForEditor(cm, name, handle) { for (var i = 0; i < cm.state.keyMaps.length; i++) { var result = lookupKey(name, cm.state.keyMaps[i], handle, cm); if (result) { return result } } return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) || lookupKey(name, cm.options.keyMap, handle, cm) } // Note that, despite the name, this function is also used to check // for bound mouse clicks. var stopSeq = new Delayed; function dispatchKey(cm, name, e, handle) { var seq = cm.state.keySeq; if (seq) { if (isModifierKey(name)) { return "handled" } if (/\'$/.test(name)) { cm.state.keySeq = null; } else { stopSeq.set(50, function () { if (cm.state.keySeq == seq) { cm.state.keySeq = null; cm.display.input.reset(); } }); } if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true } } return dispatchKeyInner(cm, name, e, handle) } function dispatchKeyInner(cm, name, e, handle) { var result = lookupKeyForEditor(cm, name, handle); if (result == "multi") { cm.state.keySeq = name; } if (result == "handled") { signalLater(cm, "keyHandled", cm, name, e); } if (result == "handled" || result == "multi") { e_preventDefault(e); restartBlink(cm); } return !!result } // Handle a key from the keydown event. function handleKeyBinding(cm, e) { var name = keyName(e, true); if (!name) { return false } if (e.shiftKey && !cm.state.keySeq) { // First try to resolve full name (including 'Shift-'). Failing // that, see if there is a cursor-motion command (starting with // 'go') bound to the keyname without 'Shift-'. return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); }) || dispatchKey(cm, name, e, function (b) { if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) { return doHandleBinding(cm, b) } }) } else { return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); }) } } // Handle a key from the keypress event function handleCharBinding(cm, e, ch) { return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); }) } var lastStoppedKey = null; function onKeyDown(e) { var cm = this; cm.curOp.focus = activeElt(); if (signalDOMEvent(cm, e)) { return } // IE does strange things with escape. if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; } var code = e.keyCode; cm.display.shift = code == 16 || e.shiftKey; var handled = handleKeyBinding(cm, e); if (presto) { lastStoppedKey = handled ? code : null; // Opera has no cut event... we try to at least catch the key combo if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) { cm.replaceSelection("", null, "cut"); } } if (gecko && !mac && !handled && code == 46 && e.shiftKey && !e.ctrlKey && document.execCommand) { document.execCommand("cut"); } // Turn mouse into crosshair when Alt is held on Mac. if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) { showCrossHair(cm); } } function showCrossHair(cm) { var lineDiv = cm.display.lineDiv; addClass(lineDiv, "CodeMirror-crosshair"); function up(e) { if (e.keyCode == 18 || !e.altKey) { rmClass(lineDiv, "CodeMirror-crosshair"); off(document, "keyup", up); off(document, "mouseover", up); } } on(document, "keyup", up); on(document, "mouseover", up); } function onKeyUp(e) { if (e.keyCode == 16) { this.doc.sel.shift = false; } signalDOMEvent(this, e); } function onKeyPress(e) { var cm = this; if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return } var keyCode = e.keyCode, charCode = e.charCode; if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return} if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return } var ch = String.fromCharCode(charCode == null ? keyCode : charCode); // Some browsers fire keypress events for backspace if (ch == "\x08") { return } if (handleCharBinding(cm, e, ch)) { return } cm.display.input.onKeyPress(e); } var DOUBLECLICK_DELAY = 400; var PastClick = function(time, pos, button) { this.time = time; this.pos = pos; this.button = button; }; PastClick.prototype.compare = function (time, pos, button) { return this.time + DOUBLECLICK_DELAY > time && cmp(pos, this.pos) == 0 && button == this.button }; var lastClick, lastDoubleClick; function clickRepeat(pos, button) { var now = +new Date; if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) { lastClick = lastDoubleClick = null; return "triple" } else if (lastClick && lastClick.compare(now, pos, button)) { lastDoubleClick = new PastClick(now, pos, button); lastClick = null; return "double" } else { lastClick = new PastClick(now, pos, button); lastDoubleClick = null; return "single" } } // A mouse down can be a single click, double click, triple click, // start of selection drag, start of text drag, new cursor // (ctrl-click), rectangle drag (alt-drag), or xwin // middle-click-paste. Or it might be a click on something we should // not interfere with, such as a scrollbar or widget. function onMouseDown(e) { var cm = this, display = cm.display; if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return } display.input.ensurePolled(); display.shift = e.shiftKey; if (eventInWidget(display, e)) { if (!webkit) { // Briefly turn off draggability, to allow widgets to do // normal dragging things. display.scroller.draggable = false; setTimeout(function () { return display.scroller.draggable = true; }, 100); } return } if (clickInGutter(cm, e)) { return } var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single"; window.focus(); // #3261: make sure, that we're not starting a second selection if (button == 1 && cm.state.selectingText) { cm.state.selectingText(e); } if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return } if (button == 1) { if (pos) { leftButtonDown(cm, pos, repeat, e); } else if (e_target(e) == display.scroller) { e_preventDefault(e); } } else if (button == 2) { if (pos) { extendSelection(cm.doc, pos); } setTimeout(function () { return display.input.focus(); }, 20); } else if (button == 3) { if (captureRightClick) { cm.display.input.onContextMenu(e); } else { delayBlurEvent(cm); } } } function handleMappedButton(cm, button, pos, repeat, event) { var name = "Click"; if (repeat == "double") { name = "Double" + name; } else if (repeat == "triple") { name = "Triple" + name; } name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name; return dispatchKey(cm, addModifierNames(name, event), event, function (bound) { if (typeof bound == "string") { bound = commands[bound]; } if (!bound) { return false } var done = false; try { if (cm.isReadOnly()) { cm.state.suppressEdits = true; } done = bound(cm, pos) != Pass; } finally { cm.state.suppressEdits = false; } return done }) } function configureMouse(cm, repeat, event) { var option = cm.getOption("configureMouse"); var value = option ? option(cm, repeat, event) : {}; if (value.unit == null) { var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey; value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line"; } if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; } if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; } if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); } return value } function leftButtonDown(cm, pos, repeat, event) { if (ie) { setTimeout(bind(ensureFocus, cm), 0); } else { cm.curOp.focus = activeElt(); } var behavior = configureMouse(cm, repeat, event); var sel = cm.doc.sel, contained; if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() && repeat == "single" && (contained = sel.contains(pos)) > -1 && (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) && (cmp(contained.to(), pos) > 0 || pos.xRel < 0)) { leftButtonStartDrag(cm, event, pos, behavior); } else { leftButtonSelect(cm, event, pos, behavior); } } // Start a text drag. When it ends, see if any dragging actually // happen, and treat as a click if it didn't. function leftButtonStartDrag(cm, event, pos, behavior) { var display = cm.display, moved = false; var dragEnd = operation(cm, function (e) { if (webkit) { display.scroller.draggable = false; } cm.state.draggingText = false; off(display.wrapper.ownerDocument, "mouseup", dragEnd); off(display.wrapper.ownerDocument, "mousemove", mouseMove); off(display.scroller, "dragstart", dragStart); off(display.scroller, "drop", dragEnd); if (!moved) { e_preventDefault(e); if (!behavior.addNew) { extendSelection(cm.doc, pos, null, null, behavior.extend); } // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) if (webkit || ie && ie_version == 9) { setTimeout(function () {display.wrapper.ownerDocument.body.focus(); display.input.focus();}, 20); } else { display.input.focus(); } } }); var mouseMove = function(e2) { moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10; }; var dragStart = function () { return moved = true; }; // Let the drag handler handle this. if (webkit) { display.scroller.draggable = true; } cm.state.draggingText = dragEnd; dragEnd.copy = !behavior.moveOnDrag; // IE's approach to draggable if (display.scroller.dragDrop) { display.scroller.dragDrop(); } on(display.wrapper.ownerDocument, "mouseup", dragEnd); on(display.wrapper.ownerDocument, "mousemove", mouseMove); on(display.scroller, "dragstart", dragStart); on(display.scroller, "drop", dragEnd); delayBlurEvent(cm); setTimeout(function () { return display.input.focus(); }, 20); } function rangeForUnit(cm, pos, unit) { if (unit == "char") { return new Range(pos, pos) } if (unit == "word") { return cm.findWordAt(pos) } if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) } var result = unit(cm, pos); return new Range(result.from, result.to) } // Normal selection, as opposed to text dragging. function leftButtonSelect(cm, event, start, behavior) { var display = cm.display, doc = cm.doc; e_preventDefault(event); var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges; if (behavior.addNew && !behavior.extend) { ourIndex = doc.sel.contains(start); if (ourIndex > -1) { ourRange = ranges[ourIndex]; } else { ourRange = new Range(start, start); } } else { ourRange = doc.sel.primary(); ourIndex = doc.sel.primIndex; } if (behavior.unit == "rectangle") { if (!behavior.addNew) { ourRange = new Range(start, start); } start = posFromMouse(cm, event, true, true); ourIndex = -1; } else { var range = rangeForUnit(cm, start, behavior.unit); if (behavior.extend) { ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend); } else { ourRange = range; } } if (!behavior.addNew) { ourIndex = 0; setSelection(doc, new Selection([ourRange], 0), sel_mouse); startSel = doc.sel; } else if (ourIndex == -1) { ourIndex = ranges.length; setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex), {scroll: false, origin: "*mouse"}); } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) { setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0), {scroll: false, origin: "*mouse"}); startSel = doc.sel; } else { replaceOneSelection(doc, ourIndex, ourRange, sel_mouse); } var lastPos = start; function extendTo(pos) { if (cmp(lastPos, pos) == 0) { return } lastPos = pos; if (behavior.unit == "rectangle") { var ranges = [], tabSize = cm.options.tabSize; var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize); var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize); var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol); for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); line <= end; line++) { var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize); if (left == right) { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); } else if (text.length > leftPos) { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); } } if (!ranges.length) { ranges.push(new Range(start, start)); } setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), {origin: "*mouse", scroll: false}); cm.scrollIntoView(pos); } else { var oldRange = ourRange; var range = rangeForUnit(cm, pos, behavior.unit); var anchor = oldRange.anchor, head; if (cmp(range.anchor, anchor) > 0) { head = range.head; anchor = minPos(oldRange.from(), range.anchor); } else { head = range.anchor; anchor = maxPos(oldRange.to(), range.head); } var ranges$1 = startSel.ranges.slice(0); ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head)); setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse); } } var editorSize = display.wrapper.getBoundingClientRect(); // Used to ensure timeout re-tries don't fire when another extend // happened in the meantime (clearTimeout isn't reliable -- at // least on Chrome, the timeouts still happen even when cleared, // if the clear happens after their scheduled firing time). var counter = 0; function extend(e) { var curCount = ++counter; var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle"); if (!cur) { return } if (cmp(cur, lastPos) != 0) { cm.curOp.focus = activeElt(); extendTo(cur); var visible = visibleLines(display, doc); if (cur.line >= visible.to || cur.line < visible.from) { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); } } else { var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0; if (outside) { setTimeout(operation(cm, function () { if (counter != curCount) { return } display.scroller.scrollTop += outside; extend(e); }), 50); } } } function done(e) { cm.state.selectingText = false; counter = Infinity; // If e is null or undefined we interpret this as someone trying // to explicitly cancel the selection rather than the user // letting go of the mouse button. if (e) { e_preventDefault(e); display.input.focus(); } off(display.wrapper.ownerDocument, "mousemove", move); off(display.wrapper.ownerDocument, "mouseup", up); doc.history.lastSelOrigin = null; } var move = operation(cm, function (e) { if (e.buttons === 0 || !e_button(e)) { done(e); } else { extend(e); } }); var up = operation(cm, done); cm.state.selectingText = up; on(display.wrapper.ownerDocument, "mousemove", move); on(display.wrapper.ownerDocument, "mouseup", up); } // Used when mouse-selecting to adjust the anchor to the proper side // of a bidi jump depending on the visual position of the head. function bidiSimplify(cm, range) { var anchor = range.anchor; var head = range.head; var anchorLine = getLine(cm.doc, anchor.line); if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range } var order = getOrder(anchorLine); if (!order) { return range } var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]; if (part.from != anchor.ch && part.to != anchor.ch) { return range } var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1); if (boundary == 0 || boundary == order.length) { return range } // Compute the relative visual position of the head compared to the // anchor (<0 is to the left, >0 to the right) var leftSide; if (head.line != anchor.line) { leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0; } else { var headIndex = getBidiPartAt(order, head.ch, head.sticky); var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1); if (headIndex == boundary - 1 || headIndex == boundary) { leftSide = dir < 0; } else { leftSide = dir > 0; } } var usePart = order[boundary + (leftSide ? -1 : 0)]; var from = leftSide == (usePart.level == 1); var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before"; return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head) } // Determines whether an event happened in the gutter, and fires the // handlers for the corresponding event. function gutterEvent(cm, e, type, prevent) { var mX, mY; if (e.touches) { mX = e.touches[0].clientX; mY = e.touches[0].clientY; } else { try { mX = e.clientX; mY = e.clientY; } catch(e) { return false } } if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false } if (prevent) { e_preventDefault(e); } var display = cm.display; var lineBox = display.lineDiv.getBoundingClientRect(); if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) } mY -= lineBox.top - display.viewOffset; for (var i = 0; i < cm.display.gutterSpecs.length; ++i) { var g = display.gutters.childNodes[i]; if (g && g.getBoundingClientRect().right >= mX) { var line = lineAtHeight(cm.doc, mY); var gutter = cm.display.gutterSpecs[i]; signal(cm, type, cm, line, gutter.className, e); return e_defaultPrevented(e) } } } function clickInGutter(cm, e) { return gutterEvent(cm, e, "gutterClick", true) } // CONTEXT MENU HANDLING // To make the context menu work, we need to briefly unhide the // textarea (making it as unobtrusive as possible) to let the // right-click take effect on it. function onContextMenu(cm, e) { if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return } if (signalDOMEvent(cm, e, "contextmenu")) { return } if (!captureRightClick) { cm.display.input.onContextMenu(e); } } function contextMenuInGutter(cm, e) { if (!hasHandler(cm, "gutterContextMenu")) { return false } return gutterEvent(cm, e, "gutterContextMenu", false) } function themeChanged(cm) { cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-"); clearCaches(cm); } var Init = {toString: function(){return "CodeMirror.Init"}}; var defaults = {}; var optionHandlers = {}; function defineOptions(CodeMirror) { var optionHandlers = CodeMirror.optionHandlers; function option(name, deflt, handle, notOnInit) { CodeMirror.defaults[name] = deflt; if (handle) { optionHandlers[name] = notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; } } CodeMirror.defineOption = option; // Passed to option handlers when there is no old value. CodeMirror.Init = Init; // These two are, on init, called from the constructor because they // have to be initialized before the editor can start at all. option("value", "", function (cm, val) { return cm.setValue(val); }, true); option("mode", null, function (cm, val) { cm.doc.modeOption = val; loadMode(cm); }, true); option("indentUnit", 2, loadMode, true); option("indentWithTabs", false); option("smartIndent", true); option("tabSize", 4, function (cm) { resetModeState(cm); clearCaches(cm); regChange(cm); }, true); option("lineSeparator", null, function (cm, val) { cm.doc.lineSep = val; if (!val) { return } var newBreaks = [], lineNo = cm.doc.first; cm.doc.iter(function (line) { for (var pos = 0;;) { var found = line.text.indexOf(val, pos); if (found == -1) { break } pos = found + val.length; newBreaks.push(Pos(lineNo, found)); } lineNo++; }); for (var i = newBreaks.length - 1; i >= 0; i--) { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); } }); option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g, function (cm, val, old) { cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); if (old != Init) { cm.refresh(); } }); option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true); option("electricChars", true); option("inputStyle", mobile ? "contenteditable" : "textarea", function () { throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME }, true); option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true); option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true); option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true); option("rtlMoveVisually", !windows); option("wholeLineUpdateBefore", true); option("theme", "default", function (cm) { themeChanged(cm); updateGutters(cm); }, true); option("keyMap", "default", function (cm, val, old) { var next = getKeyMap(val); var prev = old != Init && getKeyMap(old); if (prev && prev.detach) { prev.detach(cm, next); } if (next.attach) { next.attach(cm, prev || null); } }); option("extraKeys", null); option("configureMouse", null); option("lineWrapping", false, wrappingChanged, true); option("gutters", [], function (cm, val) { cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers); updateGutters(cm); }, true); option("fixedGutter", true, function (cm, val) { cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"; cm.refresh(); }, true); option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true); option("scrollbarStyle", "native", function (cm) { initScrollbars(cm); updateScrollbars(cm); cm.display.scrollbars.setScrollTop(cm.doc.scrollTop); cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft); }, true); option("lineNumbers", false, function (cm, val) { cm.display.gutterSpecs = getGutters(cm.options.gutters, val); updateGutters(cm); }, true); option("firstLineNumber", 1, updateGutters, true); option("lineNumberFormatter", function (integer) { return integer; }, updateGutters, true); option("showCursorWhenSelecting", false, updateSelection, true); option("resetSelectionOnContextMenu", true); option("lineWiseCopyCut", true); option("pasteLinesPerSelection", true); option("selectionsMayTouch", false); option("readOnly", false, function (cm, val) { if (val == "nocursor") { onBlur(cm); cm.display.input.blur(); } cm.display.input.readOnlyChanged(val); }); option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true); option("dragDrop", true, dragDropChanged); option("allowDropFileTypes", null); option("cursorBlinkRate", 530); option("cursorScrollMargin", 0); option("cursorHeight", 1, updateSelection, true); option("singleCursorHeightPerLine", true, updateSelection, true); option("workTime", 100); option("workDelay", 100); option("flattenSpans", true, resetModeState, true); option("addModeClass", false, resetModeState, true); option("pollInterval", 100); option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; }); option("historyEventDelay", 1250); option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true); option("maxHighlightLength", 10000, resetModeState, true); option("moveInputWithCursor", true, function (cm, val) { if (!val) { cm.display.input.resetPosition(); } }); option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; }); option("autofocus", null); option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true); option("phrases", null); } function dragDropChanged(cm, value, old) { var wasOn = old && old != Init; if (!value != !wasOn) { var funcs = cm.display.dragFunctions; var toggle = value ? on : off; toggle(cm.display.scroller, "dragstart", funcs.start); toggle(cm.display.scroller, "dragenter", funcs.enter); toggle(cm.display.scroller, "dragover", funcs.over); toggle(cm.display.scroller, "dragleave", funcs.leave); toggle(cm.display.scroller, "drop", funcs.drop); } } function wrappingChanged(cm) { if (cm.options.lineWrapping) { addClass(cm.display.wrapper, "CodeMirror-wrap"); cm.display.sizer.style.minWidth = ""; cm.display.sizerWidth = null; } else { rmClass(cm.display.wrapper, "CodeMirror-wrap"); findMaxLine(cm); } estimateLineHeights(cm); regChange(cm); clearCaches(cm); setTimeout(function () { return updateScrollbars(cm); }, 100); } // A CodeMirror instance represents an editor. This is the object // that user code is usually dealing with. function CodeMirror(place, options) { var this$1 = this; if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) } this.options = options = options ? copyObj(options) : {}; // Determine effective options based on given values and defaults. copyObj(defaults, options, false); var doc = options.value; if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); } else if (options.mode) { doc.modeOption = options.mode; } this.doc = doc; var input = new CodeMirror.inputStyles[options.inputStyle](this); var display = this.display = new Display(place, doc, input, options); display.wrapper.CodeMirror = this; themeChanged(this); if (options.lineWrapping) { this.display.wrapper.className += " CodeMirror-wrap"; } initScrollbars(this); this.state = { keyMaps: [], // stores maps added by addKeyMap overlays: [], // highlighting overlays, as added by addOverlay modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info overwrite: false, delayingBlurEvent: false, focused: false, suppressEdits: false, // used to disable editing during key handlers when in readOnly mode pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll selectingText: false, draggingText: false, highlight: new Delayed(), // stores highlight worker timeout keySeq: null, // Unfinished key sequence specialChars: null }; if (options.autofocus && !mobile) { display.input.focus(); } // Override magic textarea content restore that IE sometimes does // on our hidden textarea on reload if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); } registerEventHandlers(this); ensureGlobalHandlers(); startOperation(this); this.curOp.forceUpdate = true; attachDoc(this, doc); if ((options.autofocus && !mobile) || this.hasFocus()) { setTimeout(bind(onFocus, this), 20); } else { onBlur(this); } for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt)) { optionHandlers[opt](this, options[opt], Init); } } maybeUpdateLineNumberWidth(this); if (options.finishInit) { options.finishInit(this); } for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this); } endOperation(this); // Suppress optimizelegibility in Webkit, since it breaks text // measuring on line wrapping boundaries. if (webkit && options.lineWrapping && getComputedStyle(display.lineDiv).textRendering == "optimizelegibility") { display.lineDiv.style.textRendering = "auto"; } } // The default configuration options. CodeMirror.defaults = defaults; // Functions to run when options are changed. CodeMirror.optionHandlers = optionHandlers; // Attach the necessary event handlers when initializing the editor function registerEventHandlers(cm) { var d = cm.display; on(d.scroller, "mousedown", operation(cm, onMouseDown)); // Older IE's will not fire a second mousedown for a double click if (ie && ie_version < 11) { on(d.scroller, "dblclick", operation(cm, function (e) { if (signalDOMEvent(cm, e)) { return } var pos = posFromMouse(cm, e); if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return } e_preventDefault(e); var word = cm.findWordAt(pos); extendSelection(cm.doc, word.anchor, word.head); })); } else { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); } // Some browsers fire contextmenu *after* opening the menu, at // which point we can't mess with it anymore. Context menu is // handled in onMouseDown for these browsers. on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }); on(d.input.getField(), "contextmenu", function (e) { if (!d.scroller.contains(e.target)) { onContextMenu(cm, e); } }); // Used to suppress mouse event handling when a touch happens var touchFinished, prevTouch = {end: 0}; function finishTouch() { if (d.activeTouch) { touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000); prevTouch = d.activeTouch; prevTouch.end = +new Date; } } function isMouseLikeTouchEvent(e) { if (e.touches.length != 1) { return false } var touch = e.touches[0]; return touch.radiusX <= 1 && touch.radiusY <= 1 } function farAway(touch, other) { if (other.left == null) { return true } var dx = other.left - touch.left, dy = other.top - touch.top; return dx * dx + dy * dy > 20 * 20 } on(d.scroller, "touchstart", function (e) { if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) { d.input.ensurePolled(); clearTimeout(touchFinished); var now = +new Date; d.activeTouch = {start: now, moved: false, prev: now - prevTouch.end <= 300 ? prevTouch : null}; if (e.touches.length == 1) { d.activeTouch.left = e.touches[0].pageX; d.activeTouch.top = e.touches[0].pageY; } } }); on(d.scroller, "touchmove", function () { if (d.activeTouch) { d.activeTouch.moved = true; } }); on(d.scroller, "touchend", function (e) { var touch = d.activeTouch; if (touch && !eventInWidget(d, e) && touch.left != null && !touch.moved && new Date - touch.start < 300) { var pos = cm.coordsChar(d.activeTouch, "page"), range; if (!touch.prev || farAway(touch, touch.prev)) // Single tap { range = new Range(pos, pos); } else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap { range = cm.findWordAt(pos); } else // Triple tap { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); } cm.setSelection(range.anchor, range.head); cm.focus(); e_preventDefault(e); } finishTouch(); }); on(d.scroller, "touchcancel", finishTouch); // Sync scrolling between fake scrollbars and real scrollable // area, ensure viewport is updated when scrolling. on(d.scroller, "scroll", function () { if (d.scroller.clientHeight) { updateScrollTop(cm, d.scroller.scrollTop); setScrollLeft(cm, d.scroller.scrollLeft, true); signal(cm, "scroll", cm); } }); // Listen to wheel events in order to try and update the viewport on time. on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); }); on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); }); // Prevent wrapper from ever scrolling on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); d.dragFunctions = { enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }}, over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }}, start: function (e) { return onDragStart(cm, e); }, drop: operation(cm, onDrop), leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }} }; var inp = d.input.getField(); on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); }); on(inp, "keydown", operation(cm, onKeyDown)); on(inp, "keypress", operation(cm, onKeyPress)); on(inp, "focus", function (e) { return onFocus(cm, e); }); on(inp, "blur", function (e) { return onBlur(cm, e); }); } var initHooks = []; CodeMirror.defineInitHook = function (f) { return initHooks.push(f); }; // Indent the given line. The how parameter can be "smart", // "add"/null, "subtract", or "prev". When aggressive is false // (typically set to true for forced single-line indents), empty // lines are not indented, and places where the mode returns Pass // are left alone. function indentLine(cm, n, how, aggressive) { var doc = cm.doc, state; if (how == null) { how = "add"; } if (how == "smart") { // Fall back to "prev" when the mode doesn't have an indentation // method. if (!doc.mode.indent) { how = "prev"; } else { state = getContextBefore(cm, n).state; } } var tabSize = cm.options.tabSize; var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); if (line.stateAfter) { line.stateAfter = null; } var curSpaceString = line.text.match(/^\s*/)[0], indentation; if (!aggressive && !/\S/.test(line.text)) { indentation = 0; how = "not"; } else if (how == "smart") { indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); if (indentation == Pass || indentation > 150) { if (!aggressive) { return } how = "prev"; } } if (how == "prev") { if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); } else { indentation = 0; } } else if (how == "add") { indentation = curSpace + cm.options.indentUnit; } else if (how == "subtract") { indentation = curSpace - cm.options.indentUnit; } else if (typeof how == "number") { indentation = curSpace + how; } indentation = Math.max(0, indentation); var indentString = "", pos = 0; if (cm.options.indentWithTabs) { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} } if (pos < indentation) { indentString += spaceStr(indentation - pos); } if (indentString != curSpaceString) { replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); line.stateAfter = null; return true } else { // Ensure that, if the cursor was in the whitespace at the start // of the line, it is moved to the end of that space. for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) { var range = doc.sel.ranges[i$1]; if (range.head.line == n && range.head.ch < curSpaceString.length) { var pos$1 = Pos(n, curSpaceString.length); replaceOneSelection(doc, i$1, new Range(pos$1, pos$1)); break } } } } // This will be set to a {lineWise: bool, text: [string]} object, so // that, when pasting, we know what kind of selections the copied // text was made out of. var lastCopied = null; function setLastCopied(newLastCopied) { lastCopied = newLastCopied; } function applyTextInput(cm, inserted, deleted, sel, origin) { var doc = cm.doc; cm.display.shift = false; if (!sel) { sel = doc.sel; } var recent = +new Date - 200; var paste = origin == "paste" || cm.state.pasteIncoming > recent; var textLines = splitLinesAuto(inserted), multiPaste = null; // When pasting N lines into N selections, insert one line per selection if (paste && sel.ranges.length > 1) { if (lastCopied && lastCopied.text.join("\n") == inserted) { if (sel.ranges.length % lastCopied.text.length == 0) { multiPaste = []; for (var i = 0; i < lastCopied.text.length; i++) { multiPaste.push(doc.splitLines(lastCopied.text[i])); } } } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) { multiPaste = map(textLines, function (l) { return [l]; }); } } var updateInput = cm.curOp.updateInput; // Normal behavior is to insert the new text into every selection for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) { var range = sel.ranges[i$1]; var from = range.from(), to = range.to(); if (range.empty()) { if (deleted && deleted > 0) // Handle deletion { from = Pos(from.line, from.ch - deleted); } else if (cm.state.overwrite && !paste) // Handle overwrite { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); } else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted) { from = to = Pos(from.line, 0); } } var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines, origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")}; makeChange(cm.doc, changeEvent); signalLater(cm, "inputRead", cm, changeEvent); } if (inserted && !paste) { triggerElectric(cm, inserted); } ensureCursorVisible(cm); if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; } cm.curOp.typing = true; cm.state.pasteIncoming = cm.state.cutIncoming = -1; } function handlePaste(e, cm) { var pasted = e.clipboardData && e.clipboardData.getData("Text"); if (pasted) { e.preventDefault(); if (!cm.isReadOnly() && !cm.options.disableInput) { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); } return true } } function triggerElectric(cm, inserted) { // When an 'electric' character is inserted, immediately trigger a reindent if (!cm.options.electricChars || !cm.options.smartIndent) { return } var sel = cm.doc.sel; for (var i = sel.ranges.length - 1; i >= 0; i--) { var range = sel.ranges[i]; if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue } var mode = cm.getModeAt(range.head); var indented = false; if (mode.electricChars) { for (var j = 0; j < mode.electricChars.length; j++) { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { indented = indentLine(cm, range.head.line, "smart"); break } } } else if (mode.electricInput) { if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch))) { indented = indentLine(cm, range.head.line, "smart"); } } if (indented) { signalLater(cm, "electricInput", cm, range.head.line); } } } function copyableRanges(cm) { var text = [], ranges = []; for (var i = 0; i < cm.doc.sel.ranges.length; i++) { var line = cm.doc.sel.ranges[i].head.line; var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}; ranges.push(lineRange); text.push(cm.getRange(lineRange.anchor, lineRange.head)); } return {text: text, ranges: ranges} } function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) { field.setAttribute("autocorrect", autocorrect ? "" : "off"); field.setAttribute("autocapitalize", autocapitalize ? "" : "off"); field.setAttribute("spellcheck", !!spellcheck); } function hiddenTextarea() { var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none"); var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); // The textarea is kept positioned near the cursor to prevent the // fact that it'll be scrolled into view on input from scrolling // our fake cursor out of view. On webkit, when wrap=off, paste is // very slow. So make the area wide instead. if (webkit) { te.style.width = "1000px"; } else { te.setAttribute("wrap", "off"); } // If border: 0; -- iOS fails to open keyboard (issue #1287) if (ios) { te.style.border = "1px solid black"; } disableBrowserMagic(te); return div } // The publicly visible API. Note that methodOp(f) means // 'wrap f in an operation, performed on its `this` parameter'. // This is not the complete set of editor methods. Most of the // methods defined on the Doc type are also injected into // CodeMirror.prototype, for backwards compatibility and // convenience. function addEditorMethods(CodeMirror) { var optionHandlers = CodeMirror.optionHandlers; var helpers = CodeMirror.helpers = {}; CodeMirror.prototype = { constructor: CodeMirror, focus: function(){window.focus(); this.display.input.focus();}, setOption: function(option, value) { var options = this.options, old = options[option]; if (options[option] == value && option != "mode") { return } options[option] = value; if (optionHandlers.hasOwnProperty(option)) { operation(this, optionHandlers[option])(this, value, old); } signal(this, "optionChange", this, option); }, getOption: function(option) {return this.options[option]}, getDoc: function() {return this.doc}, addKeyMap: function(map, bottom) { this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map)); }, removeKeyMap: function(map) { var maps = this.state.keyMaps; for (var i = 0; i < maps.length; ++i) { if (maps[i] == map || maps[i].name == map) { maps.splice(i, 1); return true } } }, addOverlay: methodOp(function(spec, options) { var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec); if (mode.startState) { throw new Error("Overlays may not be stateful.") } insertSorted(this.state.overlays, {mode: mode, modeSpec: spec, opaque: options && options.opaque, priority: (options && options.priority) || 0}, function (overlay) { return overlay.priority; }); this.state.modeGen++; regChange(this); }), removeOverlay: methodOp(function(spec) { var overlays = this.state.overlays; for (var i = 0; i < overlays.length; ++i) { var cur = overlays[i].modeSpec; if (cur == spec || typeof spec == "string" && cur.name == spec) { overlays.splice(i, 1); this.state.modeGen++; regChange(this); return } } }), indentLine: methodOp(function(n, dir, aggressive) { if (typeof dir != "string" && typeof dir != "number") { if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; } else { dir = dir ? "add" : "subtract"; } } if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); } }), indentSelection: methodOp(function(how) { var ranges = this.doc.sel.ranges, end = -1; for (var i = 0; i < ranges.length; i++) { var range = ranges[i]; if (!range.empty()) { var from = range.from(), to = range.to(); var start = Math.max(end, from.line); end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; for (var j = start; j < end; ++j) { indentLine(this, j, how); } var newRanges = this.doc.sel.ranges; if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) { replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); } } else if (range.head.line > end) { indentLine(this, range.head.line, how, true); end = range.head.line; if (i == this.doc.sel.primIndex) { ensureCursorVisible(this); } } } }), // Fetch the parser token for a given character. Useful for hacks // that want to inspect the mode state (say, for completion). getTokenAt: function(pos, precise) { return takeToken(this, pos, precise) }, getLineTokens: function(line, precise) { return takeToken(this, Pos(line), precise, true) }, getTokenTypeAt: function(pos) { pos = clipPos(this.doc, pos); var styles = getLineStyles(this, getLine(this.doc, pos.line)); var before = 0, after = (styles.length - 1) / 2, ch = pos.ch; var type; if (ch == 0) { type = styles[2]; } else { for (;;) { var mid = (before + after) >> 1; if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; } else if (styles[mid * 2 + 1] < ch) { before = mid + 1; } else { type = styles[mid * 2 + 2]; break } } } var cut = type ? type.indexOf("overlay ") : -1; return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1) }, getModeAt: function(pos) { var mode = this.doc.mode; if (!mode.innerMode) { return mode } return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode }, getHelper: function(pos, type) { return this.getHelpers(pos, type)[0] }, getHelpers: function(pos, type) { var found = []; if (!helpers.hasOwnProperty(type)) { return found } var help = helpers[type], mode = this.getModeAt(pos); if (typeof mode[type] == "string") { if (help[mode[type]]) { found.push(help[mode[type]]); } } else if (mode[type]) { for (var i = 0; i < mode[type].length; i++) { var val = help[mode[type][i]]; if (val) { found.push(val); } } } else if (mode.helperType && help[mode.helperType]) { found.push(help[mode.helperType]); } else if (help[mode.name]) { found.push(help[mode.name]); } for (var i$1 = 0; i$1 < help._global.length; i$1++) { var cur = help._global[i$1]; if (cur.pred(mode, this) && indexOf(found, cur.val) == -1) { found.push(cur.val); } } return found }, getStateAfter: function(line, precise) { var doc = this.doc; line = clipLine(doc, line == null ? doc.first + doc.size - 1: line); return getContextBefore(this, line + 1, precise).state }, cursorCoords: function(start, mode) { var pos, range = this.doc.sel.primary(); if (start == null) { pos = range.head; } else if (typeof start == "object") { pos = clipPos(this.doc, start); } else { pos = start ? range.from() : range.to(); } return cursorCoords(this, pos, mode || "page") }, charCoords: function(pos, mode) { return charCoords(this, clipPos(this.doc, pos), mode || "page") }, coordsChar: function(coords, mode) { coords = fromCoordSystem(this, coords, mode || "page"); return coordsChar(this, coords.left, coords.top) }, lineAtHeight: function(height, mode) { height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top; return lineAtHeight(this.doc, height + this.display.viewOffset) }, heightAtLine: function(line, mode, includeWidgets) { var end = false, lineObj; if (typeof line == "number") { var last = this.doc.first + this.doc.size - 1; if (line < this.doc.first) { line = this.doc.first; } else if (line > last) { line = last; end = true; } lineObj = getLine(this.doc, line); } else { lineObj = line; } return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top + (end ? this.doc.height - heightAtLine(lineObj) : 0) }, defaultTextHeight: function() { return textHeight(this.display) }, defaultCharWidth: function() { return charWidth(this.display) }, getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}}, addWidget: function(pos, node, scroll, vert, horiz) { var display = this.display; pos = cursorCoords(this, clipPos(this.doc, pos)); var top = pos.bottom, left = pos.left; node.style.position = "absolute"; node.setAttribute("cm-ignore-events", "true"); this.display.input.setUneditable(node); display.sizer.appendChild(node); if (vert == "over") { top = pos.top; } else if (vert == "above" || vert == "near") { var vspace = Math.max(display.wrapper.clientHeight, this.doc.height), hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth); // Default to positioning above (if specified and possible); otherwise default to positioning below if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight) { top = pos.top - node.offsetHeight; } else if (pos.bottom + node.offsetHeight <= vspace) { top = pos.bottom; } if (left + node.offsetWidth > hspace) { left = hspace - node.offsetWidth; } } node.style.top = top + "px"; node.style.left = node.style.right = ""; if (horiz == "right") { left = display.sizer.clientWidth - node.offsetWidth; node.style.right = "0px"; } else { if (horiz == "left") { left = 0; } else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; } node.style.left = left + "px"; } if (scroll) { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); } }, triggerOnKeyDown: methodOp(onKeyDown), triggerOnKeyPress: methodOp(onKeyPress), triggerOnKeyUp: onKeyUp, triggerOnMouseDown: methodOp(onMouseDown), execCommand: function(cmd) { if (commands.hasOwnProperty(cmd)) { return commands[cmd].call(null, this) } }, triggerElectric: methodOp(function(text) { triggerElectric(this, text); }), findPosH: function(from, amount, unit, visually) { var dir = 1; if (amount < 0) { dir = -1; amount = -amount; } var cur = clipPos(this.doc, from); for (var i = 0; i < amount; ++i) { cur = findPosH(this.doc, cur, dir, unit, visually); if (cur.hitSide) { break } } return cur }, moveH: methodOp(function(dir, unit) { var this$1 = this; this.extendSelectionsBy(function (range) { if (this$1.display.shift || this$1.doc.extend || range.empty()) { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) } else { return dir < 0 ? range.from() : range.to() } }, sel_move); }), deleteH: methodOp(function(dir, unit) { var sel = this.doc.sel, doc = this.doc; if (sel.somethingSelected()) { doc.replaceSelection("", null, "+delete"); } else { deleteNearSelection(this, function (range) { var other = findPosH(doc, range.head, dir, unit, false); return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other} }); } }), findPosV: function(from, amount, unit, goalColumn) { var dir = 1, x = goalColumn; if (amount < 0) { dir = -1; amount = -amount; } var cur = clipPos(this.doc, from); for (var i = 0; i < amount; ++i) { var coords = cursorCoords(this, cur, "div"); if (x == null) { x = coords.left; } else { coords.left = x; } cur = findPosV(this, coords, dir, unit); if (cur.hitSide) { break } } return cur }, moveV: methodOp(function(dir, unit) { var this$1 = this; var doc = this.doc, goals = []; var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected(); doc.extendSelectionsBy(function (range) { if (collapse) { return dir < 0 ? range.from() : range.to() } var headPos = cursorCoords(this$1, range.head, "div"); if (range.goalColumn != null) { headPos.left = range.goalColumn; } goals.push(headPos.left); var pos = findPosV(this$1, headPos, dir, unit); if (unit == "page" && range == doc.sel.primary()) { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); } return pos }, sel_move); if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++) { doc.sel.ranges[i].goalColumn = goals[i]; } } }), // Find the word at the given position (as returned by coordsChar). findWordAt: function(pos) { var doc = this.doc, line = getLine(doc, pos.line).text; var start = pos.ch, end = pos.ch; if (line) { var helper = this.getHelper(pos, "wordChars"); if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; } var startChar = line.charAt(start); var check = isWordChar(startChar, helper) ? function (ch) { return isWordChar(ch, helper); } : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); } : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); }; while (start > 0 && check(line.charAt(start - 1))) { --start; } while (end < line.length && check(line.charAt(end))) { ++end; } } return new Range(Pos(pos.line, start), Pos(pos.line, end)) }, toggleOverwrite: function(value) { if (value != null && value == this.state.overwrite) { return } if (this.state.overwrite = !this.state.overwrite) { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); } else { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); } signal(this, "overwriteToggle", this, this.state.overwrite); }, hasFocus: function() { return this.display.input.getField() == activeElt() }, isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) }, scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }), getScrollInfo: function() { var scroller = this.display.scroller; return {left: scroller.scrollLeft, top: scroller.scrollTop, height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight, width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth, clientHeight: displayHeight(this), clientWidth: displayWidth(this)} }, scrollIntoView: methodOp(function(range, margin) { if (range == null) { range = {from: this.doc.sel.primary().head, to: null}; if (margin == null) { margin = this.options.cursorScrollMargin; } } else if (typeof range == "number") { range = {from: Pos(range, 0), to: null}; } else if (range.from == null) { range = {from: range, to: null}; } if (!range.to) { range.to = range.from; } range.margin = margin || 0; if (range.from.line != null) { scrollToRange(this, range); } else { scrollToCoordsRange(this, range.from, range.to, range.margin); } }), setSize: methodOp(function(width, height) { var this$1 = this; var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; }; if (width != null) { this.display.wrapper.style.width = interpret(width); } if (height != null) { this.display.wrapper.style.height = interpret(height); } if (this.options.lineWrapping) { clearLineMeasurementCache(this); } var lineNo = this.display.viewFrom; this.doc.iter(lineNo, this.display.viewTo, function (line) { if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widget"); break } } } ++lineNo; }); this.curOp.forceUpdate = true; signal(this, "refresh", this); }), operation: function(f){return runInOp(this, f)}, startOperation: function(){return startOperation(this)}, endOperation: function(){return endOperation(this)}, refresh: methodOp(function() { var oldHeight = this.display.cachedTextHeight; regChange(this); this.curOp.forceUpdate = true; clearCaches(this); scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop); updateGutterSpace(this.display); if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5) { estimateLineHeights(this); } signal(this, "refresh", this); }), swapDoc: methodOp(function(doc) { var old = this.doc; old.cm = null; // Cancel the current text selection if any (#5821) if (this.state.selectingText) { this.state.selectingText(); } attachDoc(this, doc); clearCaches(this); this.display.input.reset(); scrollToCoords(this, doc.scrollLeft, doc.scrollTop); this.curOp.forceScroll = true; signalLater(this, "swapDoc", this, old); return old }), phrase: function(phraseText) { var phrases = this.options.phrases; return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText }, getInputField: function(){return this.display.input.getField()}, getWrapperElement: function(){return this.display.wrapper}, getScrollerElement: function(){return this.display.scroller}, getGutterElement: function(){return this.display.gutters} }; eventMixin(CodeMirror); CodeMirror.registerHelper = function(type, name, value) { if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; } helpers[type][name] = value; }; CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { CodeMirror.registerHelper(type, name, value); helpers[type]._global.push({pred: predicate, val: value}); }; } // Used for horizontal relative motion. Dir is -1 or 1 (left or // right), unit can be "char", "column" (like char, but doesn't // cross line boundaries), "word" (across next word), or "group" (to // the start of next group of word or non-word-non-whitespace // chars). The visually param controls whether, in right-to-left // text, direction 1 means to move towards the next index in the // string, or towards the character to the right of the current // position. The resulting position will have a hitSide=true // property if it reached the end of the document. function findPosH(doc, pos, dir, unit, visually) { var oldPos = pos; var origDir = dir; var lineObj = getLine(doc, pos.line); var lineDir = visually && doc.cm && doc.cm.getOption("direction") == "rtl" ? -dir : dir; function findNextLine() { var l = pos.line + lineDir; if (l < doc.first || l >= doc.first + doc.size) { return false } pos = new Pos(l, pos.ch, pos.sticky); return lineObj = getLine(doc, l) } function moveOnce(boundToLine) { var next; if (visually) { next = moveVisually(doc.cm, lineObj, pos, dir); } else { next = moveLogically(lineObj, pos, dir); } if (next == null) { if (!boundToLine && findNextLine()) { pos = endOfLine(visually, doc.cm, lineObj, pos.line, lineDir); } else { return false } } else { pos = next; } return true } if (unit == "char") { moveOnce(); } else if (unit == "column") { moveOnce(true); } else if (unit == "word" || unit == "group") { var sawType = null, group = unit == "group"; var helper = doc.cm && doc.cm.getHelper(pos, "wordChars"); for (var first = true;; first = false) { if (dir < 0 && !moveOnce(!first)) { break } var cur = lineObj.text.charAt(pos.ch) || "\n"; var type = isWordChar(cur, helper) ? "w" : group && cur == "\n" ? "n" : !group || /\s/.test(cur) ? null : "p"; if (group && !first && !type) { type = "s"; } if (sawType && sawType != type) { if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";} break } if (type) { sawType = type; } if (dir > 0 && !moveOnce(!first)) { break } } } var result = skipAtomic(doc, pos, oldPos, origDir, true); if (equalCursorPos(oldPos, result)) { result.hitSide = true; } return result } // For relative vertical movement. Dir may be -1 or 1. Unit can be // "page" or "line". The resulting position will have a hitSide=true // property if it reached the end of the document. function findPosV(cm, pos, dir, unit) { var doc = cm.doc, x = pos.left, y; if (unit == "page") { var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight); var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3); y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount; } else if (unit == "line") { y = dir > 0 ? pos.bottom + 3 : pos.top - 3; } var target; for (;;) { target = coordsChar(cm, x, y); if (!target.outside) { break } if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break } y += dir * 5; } return target } // CONTENTEDITABLE INPUT STYLE var ContentEditableInput = function(cm) { this.cm = cm; this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null; this.polling = new Delayed(); this.composing = null; this.gracePeriod = false; this.readDOMTimeout = null; }; ContentEditableInput.prototype.init = function (display) { var this$1 = this; var input = this, cm = input.cm; var div = input.div = display.lineDiv; disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize); on(div, "paste", function (e) { if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } // IE doesn't fire input events, so we schedule a read for the pasted content in this way if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); } }); on(div, "compositionstart", function (e) { this$1.composing = {data: e.data, done: false}; }); on(div, "compositionupdate", function (e) { if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; } }); on(div, "compositionend", function (e) { if (this$1.composing) { if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); } this$1.composing.done = true; } }); on(div, "touchstart", function () { return input.forceCompositionEnd(); }); on(div, "input", function () { if (!this$1.composing) { this$1.readFromDOMSoon(); } }); function onCopyCut(e) { if (signalDOMEvent(cm, e)) { return } if (cm.somethingSelected()) { setLastCopied({lineWise: false, text: cm.getSelections()}); if (e.type == "cut") { cm.replaceSelection("", null, "cut"); } } else if (!cm.options.lineWiseCopyCut) { return } else { var ranges = copyableRanges(cm); setLastCopied({lineWise: true, text: ranges.text}); if (e.type == "cut") { cm.operation(function () { cm.setSelections(ranges.ranges, 0, sel_dontScroll); cm.replaceSelection("", null, "cut"); }); } } if (e.clipboardData) { e.clipboardData.clearData(); var content = lastCopied.text.join("\n"); // iOS exposes the clipboard API, but seems to discard content inserted into it e.clipboardData.setData("Text", content); if (e.clipboardData.getData("Text") == content) { e.preventDefault(); return } } // Old-fashioned briefly-focus-a-textarea hack var kludge = hiddenTextarea(), te = kludge.firstChild; cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild); te.value = lastCopied.text.join("\n"); var hadFocus = document.activeElement; selectInput(te); setTimeout(function () { cm.display.lineSpace.removeChild(kludge); hadFocus.focus(); if (hadFocus == div) { input.showPrimarySelection(); } }, 50); } on(div, "copy", onCopyCut); on(div, "cut", onCopyCut); }; ContentEditableInput.prototype.prepareSelection = function () { var result = prepareSelection(this.cm, false); result.focus = this.cm.state.focused; return result }; ContentEditableInput.prototype.showSelection = function (info, takeFocus) { if (!info || !this.cm.display.view.length) { return } if (info.focus || takeFocus) { this.showPrimarySelection(); } this.showMultipleSelections(info); }; ContentEditableInput.prototype.getSelection = function () { return this.cm.display.wrapper.ownerDocument.getSelection() }; ContentEditableInput.prototype.showPrimarySelection = function () { var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary(); var from = prim.from(), to = prim.to(); if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) { sel.removeAllRanges(); return } var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset); if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad && cmp(minPos(curAnchor, curFocus), from) == 0 && cmp(maxPos(curAnchor, curFocus), to) == 0) { return } var view = cm.display.view; var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) || {node: view[0].measure.map[2], offset: 0}; var end = to.line < cm.display.viewTo && posToDOM(cm, to); if (!end) { var measure = view[view.length - 1].measure; var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map; end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]}; } if (!start || !end) { sel.removeAllRanges(); return } var old = sel.rangeCount && sel.getRangeAt(0), rng; try { rng = range(start.node, start.offset, end.offset, end.node); } catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible if (rng) { if (!gecko && cm.state.focused) { sel.collapse(start.node, start.offset); if (!rng.collapsed) { sel.removeAllRanges(); sel.addRange(rng); } } else { sel.removeAllRanges(); sel.addRange(rng); } if (old && sel.anchorNode == null) { sel.addRange(old); } else if (gecko) { this.startGracePeriod(); } } this.rememberSelection(); }; ContentEditableInput.prototype.startGracePeriod = function () { var this$1 = this; clearTimeout(this.gracePeriod); this.gracePeriod = setTimeout(function () { this$1.gracePeriod = false; if (this$1.selectionChanged()) { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); } }, 20); }; ContentEditableInput.prototype.showMultipleSelections = function (info) { removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors); removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection); }; ContentEditableInput.prototype.rememberSelection = function () { var sel = this.getSelection(); this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset; this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset; }; ContentEditableInput.prototype.selectionInEditor = function () { var sel = this.getSelection(); if (!sel.rangeCount) { return false } var node = sel.getRangeAt(0).commonAncestorContainer; return contains(this.div, node) }; ContentEditableInput.prototype.focus = function () { if (this.cm.options.readOnly != "nocursor") { if (!this.selectionInEditor()) { this.showSelection(this.prepareSelection(), true); } this.div.focus(); } }; ContentEditableInput.prototype.blur = function () { this.div.blur(); }; ContentEditableInput.prototype.getField = function () { return this.div }; ContentEditableInput.prototype.supportsTouch = function () { return true }; ContentEditableInput.prototype.receivedFocus = function () { var input = this; if (this.selectionInEditor()) { this.pollSelection(); } else { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); } function poll() { if (input.cm.state.focused) { input.pollSelection(); input.polling.set(input.cm.options.pollInterval, poll); } } this.polling.set(this.cm.options.pollInterval, poll); }; ContentEditableInput.prototype.selectionChanged = function () { var sel = this.getSelection(); return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset }; ContentEditableInput.prototype.pollSelection = function () { if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return } var sel = this.getSelection(), cm = this.cm; // On Android Chrome (version 56, at least), backspacing into an // uneditable block element will put the cursor in that element, // and then, because it's not editable, hide the virtual keyboard. // Because Android doesn't allow us to actually detect backspace // presses in a sane way, this code checks for when that happens // and simulates a backspace press in this case. if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) { this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs}); this.blur(); this.focus(); return } if (this.composing) { return } this.rememberSelection(); var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); var head = domToPos(cm, sel.focusNode, sel.focusOffset); if (anchor && head) { runInOp(cm, function () { setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll); if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; } }); } }; ContentEditableInput.prototype.pollContent = function () { if (this.readDOMTimeout != null) { clearTimeout(this.readDOMTimeout); this.readDOMTimeout = null; } var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary(); var from = sel.from(), to = sel.to(); if (from.ch == 0 && from.line > cm.firstLine()) { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); } if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine()) { to = Pos(to.line + 1, 0); } if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false } var fromIndex, fromLine, fromNode; if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) { fromLine = lineNo(display.view[0].line); fromNode = display.view[0].node; } else { fromLine = lineNo(display.view[fromIndex].line); fromNode = display.view[fromIndex - 1].node.nextSibling; } var toIndex = findViewIndex(cm, to.line); var toLine, toNode; if (toIndex == display.view.length - 1) { toLine = display.viewTo - 1; toNode = display.lineDiv.lastChild; } else { toLine = lineNo(display.view[toIndex + 1].line) - 1; toNode = display.view[toIndex + 1].node.previousSibling; } if (!fromNode) { return false } var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)); var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)); while (newText.length > 1 && oldText.length > 1) { if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; } else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; } else { break } } var cutFront = 0, cutEnd = 0; var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length); while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront)) { ++cutFront; } var newBot = lst(newText), oldBot = lst(oldText); var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0), oldBot.length - (oldText.length == 1 ? cutFront : 0)); while (cutEnd < maxCutEnd && newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) { ++cutEnd; } // Try to move start of change to start of selection if ambiguous if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) { while (cutFront && cutFront > from.ch && newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) { cutFront--; cutEnd++; } } newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, ""); newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, ""); var chFrom = Pos(fromLine, cutFront); var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0); if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) { replaceRange(cm.doc, newText, chFrom, chTo, "+input"); return true } }; ContentEditableInput.prototype.ensurePolled = function () { this.forceCompositionEnd(); }; ContentEditableInput.prototype.reset = function () { this.forceCompositionEnd(); }; ContentEditableInput.prototype.forceCompositionEnd = function () { if (!this.composing) { return } clearTimeout(this.readDOMTimeout); this.composing = null; this.updateFromDOM(); this.div.blur(); this.div.focus(); }; ContentEditableInput.prototype.readFromDOMSoon = function () { var this$1 = this; if (this.readDOMTimeout != null) { return } this.readDOMTimeout = setTimeout(function () { this$1.readDOMTimeout = null; if (this$1.composing) { if (this$1.composing.done) { this$1.composing = null; } else { return } } this$1.updateFromDOM(); }, 80); }; ContentEditableInput.prototype.updateFromDOM = function () { var this$1 = this; if (this.cm.isReadOnly() || !this.pollContent()) { runInOp(this.cm, function () { return regChange(this$1.cm); }); } }; ContentEditableInput.prototype.setUneditable = function (node) { node.contentEditable = "false"; }; ContentEditableInput.prototype.onKeyPress = function (e) { if (e.charCode == 0 || this.composing) { return } e.preventDefault(); if (!this.cm.isReadOnly()) { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); } }; ContentEditableInput.prototype.readOnlyChanged = function (val) { this.div.contentEditable = String(val != "nocursor"); }; ContentEditableInput.prototype.onContextMenu = function () {}; ContentEditableInput.prototype.resetPosition = function () {}; ContentEditableInput.prototype.needsContentAttribute = true; function posToDOM(cm, pos) { var view = findViewForLine(cm, pos.line); if (!view || view.hidden) { return null } var line = getLine(cm.doc, pos.line); var info = mapFromLineView(view, line, pos.line); var order = getOrder(line, cm.doc.direction), side = "left"; if (order) { var partPos = getBidiPartAt(order, pos.ch); side = partPos % 2 ? "right" : "left"; } var result = nodeAndOffsetInLineMap(info.map, pos.ch, side); result.offset = result.collapse == "right" ? result.end : result.start; return result } function isInGutter(node) { for (var scan = node; scan; scan = scan.parentNode) { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } } return false } function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos } function domTextBetween(cm, from, to, fromLine, toLine) { var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false; function recognizeMarker(id) { return function (marker) { return marker.id == id; } } function close() { if (closing) { text += lineSep; if (extraLinebreak) { text += lineSep; } closing = extraLinebreak = false; } } function addText(str) { if (str) { close(); text += str; } } function walk(node) { if (node.nodeType == 1) { var cmText = node.getAttribute("cm-text"); if (cmText) { addText(cmText); return } var markerID = node.getAttribute("cm-marker"), range; if (markerID) { var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)); if (found.length && (range = found[0].find(0))) { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)); } return } if (node.getAttribute("contenteditable") == "false") { return } var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName); if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return } if (isBlock) { close(); } for (var i = 0; i < node.childNodes.length; i++) { walk(node.childNodes[i]); } if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; } if (isBlock) { closing = true; } } else if (node.nodeType == 3) { addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " ")); } } for (;;) { walk(from); if (from == to) { break } from = from.nextSibling; extraLinebreak = false; } return text } function domToPos(cm, node, offset) { var lineNode; if (node == cm.display.lineDiv) { lineNode = cm.display.lineDiv.childNodes[offset]; if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) } node = null; offset = 0; } else { for (lineNode = node;; lineNode = lineNode.parentNode) { if (!lineNode || lineNode == cm.display.lineDiv) { return null } if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break } } } for (var i = 0; i < cm.display.view.length; i++) { var lineView = cm.display.view[i]; if (lineView.node == lineNode) { return locateNodeInLineView(lineView, node, offset) } } } function locateNodeInLineView(lineView, node, offset) { var wrapper = lineView.text.firstChild, bad = false; if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) } if (node == wrapper) { bad = true; node = wrapper.childNodes[offset]; offset = 0; if (!node) { var line = lineView.rest ? lst(lineView.rest) : lineView.line; return badPos(Pos(lineNo(line), line.text.length), bad) } } var textNode = node.nodeType == 3 ? node : null, topNode = node; if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { textNode = node.firstChild; if (offset) { offset = textNode.nodeValue.length; } } while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; } var measure = lineView.measure, maps = measure.maps; function find(textNode, topNode, offset) { for (var i = -1; i < (maps ? maps.length : 0); i++) { var map = i < 0 ? measure.map : maps[i]; for (var j = 0; j < map.length; j += 3) { var curNode = map[j + 2]; if (curNode == textNode || curNode == topNode) { var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]); var ch = map[j] + offset; if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)]; } return Pos(line, ch) } } } } var found = find(textNode, topNode, offset); if (found) { return badPos(found, bad) } // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) { found = find(after, after.firstChild, 0); if (found) { return badPos(Pos(found.line, found.ch - dist), bad) } else { dist += after.textContent.length; } } for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) { found = find(before, before.firstChild, -1); if (found) { return badPos(Pos(found.line, found.ch + dist$1), bad) } else { dist$1 += before.textContent.length; } } } // TEXTAREA INPUT STYLE var TextareaInput = function(cm) { this.cm = cm; // See input.poll and input.reset this.prevInput = ""; // Flag that indicates whether we expect input to appear real soon // now (after some event like 'keypress' or 'input') and are // polling intensively. this.pollingFast = false; // Self-resetting timeout for the poller this.polling = new Delayed(); // Used to work around IE issue with selection being forgotten when focus moves away from textarea this.hasSelection = false; this.composing = null; }; TextareaInput.prototype.init = function (display) { var this$1 = this; var input = this, cm = this.cm; this.createField(display); var te = this.textarea; display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild); // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore) if (ios) { te.style.width = "0px"; } on(te, "input", function () { if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; } input.poll(); }); on(te, "paste", function (e) { if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } cm.state.pasteIncoming = +new Date; input.fastPoll(); }); function prepareCopyCut(e) { if (signalDOMEvent(cm, e)) { return } if (cm.somethingSelected()) { setLastCopied({lineWise: false, text: cm.getSelections()}); } else if (!cm.options.lineWiseCopyCut) { return } else { var ranges = copyableRanges(cm); setLastCopied({lineWise: true, text: ranges.text}); if (e.type == "cut") { cm.setSelections(ranges.ranges, null, sel_dontScroll); } else { input.prevInput = ""; te.value = ranges.text.join("\n"); selectInput(te); } } if (e.type == "cut") { cm.state.cutIncoming = +new Date; } } on(te, "cut", prepareCopyCut); on(te, "copy", prepareCopyCut); on(display.scroller, "paste", function (e) { if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return } if (!te.dispatchEvent) { cm.state.pasteIncoming = +new Date; input.focus(); return } // Pass the `paste` event to the textarea so it's handled by its event listener. var event = new Event("paste"); event.clipboardData = e.clipboardData; te.dispatchEvent(event); }); // Prevent normal selection in the editor (we handle our own) on(display.lineSpace, "selectstart", function (e) { if (!eventInWidget(display, e)) { e_preventDefault(e); } }); on(te, "compositionstart", function () { var start = cm.getCursor("from"); if (input.composing) { input.composing.range.clear(); } input.composing = { start: start, range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"}) }; }); on(te, "compositionend", function () { if (input.composing) { input.poll(); input.composing.range.clear(); input.composing = null; } }); }; TextareaInput.prototype.createField = function (_display) { // Wraps and hides input textarea this.wrapper = hiddenTextarea(); // The semihidden textarea that is focused when the editor is // focused, and receives input. this.textarea = this.wrapper.firstChild; }; TextareaInput.prototype.prepareSelection = function () { // Redraw the selection and/or cursor var cm = this.cm, display = cm.display, doc = cm.doc; var result = prepareSelection(cm); // Move the hidden textarea near the cursor to prevent scrolling artifacts if (cm.options.moveInputWithCursor) { var headPos = cursorCoords(cm, doc.sel.primary().head, "div"); var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect(); result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10, headPos.top + lineOff.top - wrapOff.top)); result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10, headPos.left + lineOff.left - wrapOff.left)); } return result }; TextareaInput.prototype.showSelection = function (drawn) { var cm = this.cm, display = cm.display; removeChildrenAndAdd(display.cursorDiv, drawn.cursors); removeChildrenAndAdd(display.selectionDiv, drawn.selection); if (drawn.teTop != null) { this.wrapper.style.top = drawn.teTop + "px"; this.wrapper.style.left = drawn.teLeft + "px"; } }; // Reset the input to correspond to the selection (or to be empty, // when not typing and nothing is selected) TextareaInput.prototype.reset = function (typing) { if (this.contextMenuPending || this.composing) { return } var cm = this.cm; if (cm.somethingSelected()) { this.prevInput = ""; var content = cm.getSelection(); this.textarea.value = content; if (cm.state.focused) { selectInput(this.textarea); } if (ie && ie_version >= 9) { this.hasSelection = content; } } else if (!typing) { this.prevInput = this.textarea.value = ""; if (ie && ie_version >= 9) { this.hasSelection = null; } } }; TextareaInput.prototype.getField = function () { return this.textarea }; TextareaInput.prototype.supportsTouch = function () { return false }; TextareaInput.prototype.focus = function () { if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) { try { this.textarea.focus(); } catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM } }; TextareaInput.prototype.blur = function () { this.textarea.blur(); }; TextareaInput.prototype.resetPosition = function () { this.wrapper.style.top = this.wrapper.style.left = 0; }; TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); }; // Poll for input changes, using the normal rate of polling. This // runs as long as the editor is focused. TextareaInput.prototype.slowPoll = function () { var this$1 = this; if (this.pollingFast) { return } this.polling.set(this.cm.options.pollInterval, function () { this$1.poll(); if (this$1.cm.state.focused) { this$1.slowPoll(); } }); }; // When an event has just come in that is likely to add or change // something in the input textarea, we poll faster, to ensure that // the change appears on the screen quickly. TextareaInput.prototype.fastPoll = function () { var missed = false, input = this; input.pollingFast = true; function p() { var changed = input.poll(); if (!changed && !missed) {missed = true; input.polling.set(60, p);} else {input.pollingFast = false; input.slowPoll();} } input.polling.set(20, p); }; // Read input from the textarea, and update the document to match. // When something is selected, it is present in the textarea, and // selected (unless it is huge, in which case a placeholder is // used). When nothing is selected, the cursor sits after previously // seen text (can be empty), which is stored in prevInput (we must // not reset the textarea when typing, because that breaks IME). TextareaInput.prototype.poll = function () { var this$1 = this; var cm = this.cm, input = this.textarea, prevInput = this.prevInput; // Since this is called a *lot*, try to bail out as cheaply as // possible when it is clear that nothing happened. hasSelection // will be the case when there is a lot of text in the textarea, // in which case reading its value would be expensive. if (this.contextMenuPending || !cm.state.focused || (hasSelection(input) && !prevInput && !this.composing) || cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq) { return false } var text = input.value; // If nothing changed, bail. if (text == prevInput && !cm.somethingSelected()) { return false } // Work around nonsensical selection resetting in IE9/10, and // inexplicable appearance of private area unicode characters on // some key combos in Mac (#2689). if (ie && ie_version >= 9 && this.hasSelection === text || mac && /[\uf700-\uf7ff]/.test(text)) { cm.display.input.reset(); return false } if (cm.doc.sel == cm.display.selForContextMenu) { var first = text.charCodeAt(0); if (first == 0x200b && !prevInput) { prevInput = "\u200b"; } if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") } } // Find the part of the input that is actually new var same = 0, l = Math.min(prevInput.length, text.length); while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; } runInOp(cm, function () { applyTextInput(cm, text.slice(same), prevInput.length - same, null, this$1.composing ? "*compose" : null); // Don't leave long text in the textarea, since it makes further polling slow if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; } else { this$1.prevInput = text; } if (this$1.composing) { this$1.composing.range.clear(); this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"), {className: "CodeMirror-composing"}); } }); return true }; TextareaInput.prototype.ensurePolled = function () { if (this.pollingFast && this.poll()) { this.pollingFast = false; } }; TextareaInput.prototype.onKeyPress = function () { if (ie && ie_version >= 9) { this.hasSelection = null; } this.fastPoll(); }; TextareaInput.prototype.onContextMenu = function (e) { var input = this, cm = input.cm, display = cm.display, te = input.textarea; if (input.contextMenuPending) { input.contextMenuPending(); } var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop; if (!pos || presto) { return } // Opera is difficult. // Reset the current text selection only if the click is done outside of the selection // and 'resetSelectionOnContextMenu' option is true. var reset = cm.options.resetSelectionOnContextMenu; if (reset && cm.doc.sel.contains(pos) == -1) { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); } var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText; var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect(); input.wrapper.style.cssText = "position: static"; te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; var oldScrollY; if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712) display.input.focus(); if (webkit) { window.scrollTo(null, oldScrollY); } display.input.reset(); // Adds "Select all" to context menu in FF if (!cm.somethingSelected()) { te.value = input.prevInput = " "; } input.contextMenuPending = rehide; display.selForContextMenu = cm.doc.sel; clearTimeout(display.detectingSelectAll); // Select-all will be greyed out if there's nothing to select, so // this adds a zero-width space so that we can later check whether // it got selected. function prepareSelectAllHack() { if (te.selectionStart != null) { var selected = cm.somethingSelected(); var extval = "\u200b" + (selected ? te.value : ""); te.value = "\u21da"; // Used to catch context-menu undo te.value = extval; input.prevInput = selected ? "" : "\u200b"; te.selectionStart = 1; te.selectionEnd = extval.length; // Re-set this, in case some other handler touched the // selection in the meantime. display.selForContextMenu = cm.doc.sel; } } function rehide() { if (input.contextMenuPending != rehide) { return } input.contextMenuPending = false; input.wrapper.style.cssText = oldWrapperCSS; te.style.cssText = oldCSS; if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); } // Try to detect the user choosing select-all if (te.selectionStart != null) { if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); } var i = 0, poll = function () { if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && te.selectionEnd > 0 && input.prevInput == "\u200b") { operation(cm, selectAll)(cm); } else if (i++ < 10) { display.detectingSelectAll = setTimeout(poll, 500); } else { display.selForContextMenu = null; display.input.reset(); } }; display.detectingSelectAll = setTimeout(poll, 200); } } if (ie && ie_version >= 9) { prepareSelectAllHack(); } if (captureRightClick) { e_stop(e); var mouseup = function () { off(window, "mouseup", mouseup); setTimeout(rehide, 20); }; on(window, "mouseup", mouseup); } else { setTimeout(rehide, 50); } }; TextareaInput.prototype.readOnlyChanged = function (val) { if (!val) { this.reset(); } this.textarea.disabled = val == "nocursor"; }; TextareaInput.prototype.setUneditable = function () {}; TextareaInput.prototype.needsContentAttribute = false; function fromTextArea(textarea, options) { options = options ? copyObj(options) : {}; options.value = textarea.value; if (!options.tabindex && textarea.tabIndex) { options.tabindex = textarea.tabIndex; } if (!options.placeholder && textarea.placeholder) { options.placeholder = textarea.placeholder; } // Set autofocus to true if this textarea is focused, or if it has // autofocus and no other element is focused. if (options.autofocus == null) { var hasFocus = activeElt(); options.autofocus = hasFocus == textarea || textarea.getAttribute("autofocus") != null && hasFocus == document.body; } function save() {textarea.value = cm.getValue();} var realSubmit; if (textarea.form) { on(textarea.form, "submit", save); // Deplorable hack to make the submit method do the right thing. if (!options.leaveSubmitMethodAlone) { var form = textarea.form; realSubmit = form.submit; try { var wrappedSubmit = form.submit = function () { save(); form.submit = realSubmit; form.submit(); form.submit = wrappedSubmit; }; } catch(e) {} } } options.finishInit = function (cm) { cm.save = save; cm.getTextArea = function () { return textarea; }; cm.toTextArea = function () { cm.toTextArea = isNaN; // Prevent this from being ran twice save(); textarea.parentNode.removeChild(cm.getWrapperElement()); textarea.style.display = ""; if (textarea.form) { off(textarea.form, "submit", save); if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function") { textarea.form.submit = realSubmit; } } }; }; textarea.style.display = "none"; var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); }, options); return cm } function addLegacyProps(CodeMirror) { CodeMirror.off = off; CodeMirror.on = on; CodeMirror.wheelEventPixels = wheelEventPixels; CodeMirror.Doc = Doc; CodeMirror.splitLines = splitLinesAuto; CodeMirror.countColumn = countColumn; CodeMirror.findColumn = findColumn; CodeMirror.isWordChar = isWordCharBasic; CodeMirror.Pass = Pass; CodeMirror.signal = signal; CodeMirror.Line = Line; CodeMirror.changeEnd = changeEnd; CodeMirror.scrollbarModel = scrollbarModel; CodeMirror.Pos = Pos; CodeMirror.cmpPos = cmp; CodeMirror.modes = modes; CodeMirror.mimeModes = mimeModes; CodeMirror.resolveMode = resolveMode; CodeMirror.getMode = getMode; CodeMirror.modeExtensions = modeExtensions; CodeMirror.extendMode = extendMode; CodeMirror.copyState = copyState; CodeMirror.startState = startState; CodeMirror.innerMode = innerMode; CodeMirror.commands = commands; CodeMirror.keyMap = keyMap; CodeMirror.keyName = keyName; CodeMirror.isModifierKey = isModifierKey; CodeMirror.lookupKey = lookupKey; CodeMirror.normalizeKeyMap = normalizeKeyMap; CodeMirror.StringStream = StringStream; CodeMirror.SharedTextMarker = SharedTextMarker; CodeMirror.TextMarker = TextMarker; CodeMirror.LineWidget = LineWidget; CodeMirror.e_preventDefault = e_preventDefault; CodeMirror.e_stopPropagation = e_stopPropagation; CodeMirror.e_stop = e_stop; CodeMirror.addClass = addClass; CodeMirror.contains = contains; CodeMirror.rmClass = rmClass; CodeMirror.keyNames = keyNames; } // EDITOR CONSTRUCTOR defineOptions(CodeMirror); addEditorMethods(CodeMirror); // Set up methods on CodeMirror's prototype to redirect to the editor's document. var dontDelegate = "iter insert remove copy getEditor constructor".split(" "); for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) { CodeMirror.prototype[prop] = (function(method) { return function() {return method.apply(this.doc, arguments)} })(Doc.prototype[prop]); } } eventMixin(Doc); CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}; // Extra arguments are stored as the mode's dependencies, which is // used by (legacy) mechanisms like loadmode.js to automatically // load a mode. (Preferred mechanism is the require/define calls.) CodeMirror.defineMode = function(name/*, mode, …*/) { if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; } defineMode.apply(this, arguments); }; CodeMirror.defineMIME = defineMIME; // Minimal default mode. CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); }); CodeMirror.defineMIME("text/plain", "null"); // EXTENSIONS CodeMirror.defineExtension = function (name, func) { CodeMirror.prototype[name] = func; }; CodeMirror.defineDocExtension = function (name, func) { Doc.prototype[name] = func; }; CodeMirror.fromTextArea = fromTextArea; addLegacyProps(CodeMirror); CodeMirror.version = "5.51.0"; return CodeMirror; }))); }); var mousetrap = createCommonjsModule(function (module) { /*global define:false */ /** * Copyright 2012-2017 Craig Campbell * * 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. * * Mousetrap is a simple keyboard shortcut library for Javascript with * no external dependencies * * @version 1.6.5 * @url craig.is/killing/mice */ (function(window, document, undefined$1) { // Check if mousetrap is used inside browser, if not, return if (!window) { return; } /** * mapping of special keycodes to their corresponding keys * * everything in this dictionary cannot use keypress events * so it has to be here to map to the correct keycodes for * keyup/keydown events * * @type {Object} */ var _MAP = { 8: 'backspace', 9: 'tab', 13: 'enter', 16: 'shift', 17: 'ctrl', 18: 'alt', 20: 'capslock', 27: 'esc', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down', 45: 'ins', 46: 'del', 91: 'meta', 93: 'meta', 224: 'meta' }; /** * mapping for special characters so they can support * * this dictionary is only used incase you want to bind a * keyup or keydown event to one of these keys * * @type {Object} */ var _KEYCODE_MAP = { 106: '*', 107: '+', 109: '-', 110: '.', 111 : '/', 186: ';', 187: '=', 188: ',', 189: '-', 190: '.', 191: '/', 192: '`', 219: '[', 220: '\\', 221: ']', 222: '\'' }; /** * this is a mapping of keys that require shift on a US keypad * back to the non shift equivelents * * this is so you can use keyup events with these keys * * note that this will only work reliably on US keyboards * * @type {Object} */ var _SHIFT_MAP = { '~': '`', '!': '1', '@': '2', '#': '3', '$': '4', '%': '5', '^': '6', '&': '7', '*': '8', '(': '9', ')': '0', '_': '-', '+': '=', ':': ';', '\"': '\'', '<': ',', '>': '.', '?': '/', '|': '\\' }; /** * this is a list of special strings you can use to map * to modifier keys when you specify your keyboard shortcuts * * @type {Object} */ var _SPECIAL_ALIASES = { 'option': 'alt', 'command': 'meta', 'return': 'enter', 'escape': 'esc', 'plus': '+', 'mod': /Mac|iPod|iPhone|iPad/.test(navigator.platform) ? 'meta' : 'ctrl' }; /** * variable to store the flipped version of _MAP from above * needed to check if we should use keypress or not when no action * is specified * * @type {Object|undefined} */ var _REVERSE_MAP; /** * loop through the f keys, f1 to f19 and add them to the map * programatically */ for (var i = 1; i < 20; ++i) { _MAP[111 + i] = 'f' + i; } /** * loop through to map numbers on the numeric keypad */ for (i = 0; i <= 9; ++i) { // This needs to use a string cause otherwise since 0 is falsey // mousetrap will never fire for numpad 0 pressed as part of a keydown // event. // // @see https://github.com/ccampbell/mousetrap/pull/258 _MAP[i + 96] = i.toString(); } /** * cross browser add event method * * @param {Element|HTMLDocument} object * @param {string} type * @param {Function} callback * @returns void */ function _addEvent(object, type, callback) { if (object.addEventListener) { object.addEventListener(type, callback, false); return; } object.attachEvent('on' + type, callback); } /** * takes the event and returns the key character * * @param {Event} e * @return {string} */ function _characterFromEvent(e) { // for keypress events we should return the character as is if (e.type == 'keypress') { var character = String.fromCharCode(e.which); // if the shift key is not pressed then it is safe to assume // that we want the character to be lowercase. this means if // you accidentally have caps lock on then your key bindings // will continue to work // // the only side effect that might not be desired is if you // bind something like 'A' cause you want to trigger an // event when capital A is pressed caps lock will no longer // trigger the event. shift+a will though. if (!e.shiftKey) { character = character.toLowerCase(); } return character; } // for non keypress events the special maps are needed if (_MAP[e.which]) { return _MAP[e.which]; } if (_KEYCODE_MAP[e.which]) { return _KEYCODE_MAP[e.which]; } // if it is not in the special map // with keydown and keyup events the character seems to always // come in as an uppercase character whether you are pressing shift // or not. we should make sure it is always lowercase for comparisons return String.fromCharCode(e.which).toLowerCase(); } /** * checks if two arrays are equal * * @param {Array} modifiers1 * @param {Array} modifiers2 * @returns {boolean} */ function _modifiersMatch(modifiers1, modifiers2) { return modifiers1.sort().join(',') === modifiers2.sort().join(','); } /** * takes a key event and figures out what the modifiers are * * @param {Event} e * @returns {Array} */ function _eventModifiers(e) { var modifiers = []; if (e.shiftKey) { modifiers.push('shift'); } if (e.altKey) { modifiers.push('alt'); } if (e.ctrlKey) { modifiers.push('ctrl'); } if (e.metaKey) { modifiers.push('meta'); } return modifiers; } /** * prevents default for this event * * @param {Event} e * @returns void */ function _preventDefault(e) { if (e.preventDefault) { e.preventDefault(); return; } e.returnValue = false; } /** * stops propogation for this event * * @param {Event} e * @returns void */ function _stopPropagation(e) { if (e.stopPropagation) { e.stopPropagation(); return; } e.cancelBubble = true; } /** * determines if the keycode specified is a modifier key or not * * @param {string} key * @returns {boolean} */ function _isModifier(key) { return key == 'shift' || key == 'ctrl' || key == 'alt' || key == 'meta'; } /** * reverses the map lookup so that we can look for specific keys * to see what can and can't use keypress * * @return {Object} */ function _getReverseMap() { if (!_REVERSE_MAP) { _REVERSE_MAP = {}; for (var key in _MAP) { // pull out the numeric keypad from here cause keypress should // be able to detect the keys from the character if (key > 95 && key < 112) { continue; } if (_MAP.hasOwnProperty(key)) { _REVERSE_MAP[_MAP[key]] = key; } } } return _REVERSE_MAP; } /** * picks the best action based on the key combination * * @param {string} key - character for key * @param {Array} modifiers * @param {string=} action passed in */ function _pickBestAction(key, modifiers, action) { // if no action was picked in we should try to pick the one // that we think would work best for this key if (!action) { action = _getReverseMap()[key] ? 'keydown' : 'keypress'; } // modifier keys don't work as expected with keypress, // switch to keydown if (action == 'keypress' && modifiers.length) { action = 'keydown'; } return action; } /** * Converts from a string key combination to an array * * @param {string} combination like "command+shift+l" * @return {Array} */ function _keysFromString(combination) { if (combination === '+') { return ['+']; } combination = combination.replace(/\+{2}/g, '+plus'); return combination.split('+'); } /** * Gets info for a specific key combination * * @param {string} combination key combination ("command+s" or "a" or "*") * @param {string=} action * @returns {Object} */ function _getKeyInfo(combination, action) { var keys; var key; var i; var modifiers = []; // take the keys from this pattern and figure out what the actual // pattern is all about keys = _keysFromString(combination); for (i = 0; i < keys.length; ++i) { key = keys[i]; // normalize key names if (_SPECIAL_ALIASES[key]) { key = _SPECIAL_ALIASES[key]; } // if this is not a keypress event then we should // be smart about using shift keys // this will only work for US keyboards however if (action && action != 'keypress' && _SHIFT_MAP[key]) { key = _SHIFT_MAP[key]; modifiers.push('shift'); } // if this key is a modifier then add it to the list of modifiers if (_isModifier(key)) { modifiers.push(key); } } // depending on what the key combination is // we will try to pick the best event for it action = _pickBestAction(key, modifiers, action); return { key: key, modifiers: modifiers, action: action }; } function _belongsTo(element, ancestor) { if (element === null || element === document) { return false; } if (element === ancestor) { return true; } return _belongsTo(element.parentNode, ancestor); } function Mousetrap(targetElement) { var self = this; targetElement = targetElement || document; if (!(self instanceof Mousetrap)) { return new Mousetrap(targetElement); } /** * element to attach key events to * * @type {Element} */ self.target = targetElement; /** * a list of all the callbacks setup via Mousetrap.bind() * * @type {Object} */ self._callbacks = {}; /** * direct map of string combinations to callbacks used for trigger() * * @type {Object} */ self._directMap = {}; /** * keeps track of what level each sequence is at since multiple * sequences can start out with the same sequence * * @type {Object} */ var _sequenceLevels = {}; /** * variable to store the setTimeout call * * @type {null|number} */ var _resetTimer; /** * temporary state where we will ignore the next keyup * * @type {boolean|string} */ var _ignoreNextKeyup = false; /** * temporary state where we will ignore the next keypress * * @type {boolean} */ var _ignoreNextKeypress = false; /** * are we currently inside of a sequence? * type of action ("keyup" or "keydown" or "keypress") or false * * @type {boolean|string} */ var _nextExpectedAction = false; /** * resets all sequence counters except for the ones passed in * * @param {Object} doNotReset * @returns void */ function _resetSequences(doNotReset) { doNotReset = doNotReset || {}; var activeSequences = false, key; for (key in _sequenceLevels) { if (doNotReset[key]) { activeSequences = true; continue; } _sequenceLevels[key] = 0; } if (!activeSequences) { _nextExpectedAction = false; } } /** * finds all callbacks that match based on the keycode, modifiers, * and action * * @param {string} character * @param {Array} modifiers * @param {Event|Object} e * @param {string=} sequenceName - name of the sequence we are looking for * @param {string=} combination * @param {number=} level * @returns {Array} */ function _getMatches(character, modifiers, e, sequenceName, combination, level) { var i; var callback; var matches = []; var action = e.type; // if there are no events related to this keycode if (!self._callbacks[character]) { return []; } // if a modifier key is coming up on its own we should allow it if (action == 'keyup' && _isModifier(character)) { modifiers = [character]; } // loop through all callbacks for the key that was pressed // and see if any of them match for (i = 0; i < self._callbacks[character].length; ++i) { callback = self._callbacks[character][i]; // if a sequence name is not specified, but this is a sequence at // the wrong level then move onto the next match if (!sequenceName && callback.seq && _sequenceLevels[callback.seq] != callback.level) { continue; } // if the action we are looking for doesn't match the action we got // then we should keep going if (action != callback.action) { continue; } // if this is a keypress event and the meta key and control key // are not pressed that means that we need to only look at the // character, otherwise check the modifiers as well // // chrome will not fire a keypress if meta or control is down // safari will fire a keypress if meta or meta+shift is down // firefox will fire a keypress if meta or control is down if ((action == 'keypress' && !e.metaKey && !e.ctrlKey) || _modifiersMatch(modifiers, callback.modifiers)) { // when you bind a combination or sequence a second time it // should overwrite the first one. if a sequenceName or // combination is specified in this call it does just that // // @todo make deleting its own method? var deleteCombo = !sequenceName && callback.combo == combination; var deleteSequence = sequenceName && callback.seq == sequenceName && callback.level == level; if (deleteCombo || deleteSequence) { self._callbacks[character].splice(i, 1); } matches.push(callback); } } return matches; } /** * actually calls the callback function * * if your callback function returns false this will use the jquery * convention - prevent default and stop propogation on the event * * @param {Function} callback * @param {Event} e * @returns void */ function _fireCallback(callback, e, combo, sequence) { // if this event should not happen stop here if (self.stopCallback(e, e.target || e.srcElement, combo, sequence)) { return; } if (callback(e, combo) === false) { _preventDefault(e); _stopPropagation(e); } } /** * handles a character key event * * @param {string} character * @param {Array} modifiers * @param {Event} e * @returns void */ self._handleKey = function(character, modifiers, e) { var callbacks = _getMatches(character, modifiers, e); var i; var doNotReset = {}; var maxLevel = 0; var processedSequenceCallback = false; // Calculate the maxLevel for sequences so we can only execute the longest callback sequence for (i = 0; i < callbacks.length; ++i) { if (callbacks[i].seq) { maxLevel = Math.max(maxLevel, callbacks[i].level); } } // loop through matching callbacks for this key event for (i = 0; i < callbacks.length; ++i) { // fire for all sequence callbacks // this is because if for example you have multiple sequences // bound such as "g i" and "g t" they both need to fire the // callback for matching g cause otherwise you can only ever // match the first one if (callbacks[i].seq) { // only fire callbacks for the maxLevel to prevent // subsequences from also firing // // for example 'a option b' should not cause 'option b' to fire // even though 'option b' is part of the other sequence // // any sequences that do not match here will be discarded // below by the _resetSequences call if (callbacks[i].level != maxLevel) { continue; } processedSequenceCallback = true; // keep a list of which sequences were matches for later doNotReset[callbacks[i].seq] = 1; _fireCallback(callbacks[i].callback, e, callbacks[i].combo, callbacks[i].seq); continue; } // if there were no sequence matches but we are still here // that means this is a regular match so we should fire that if (!processedSequenceCallback) { _fireCallback(callbacks[i].callback, e, callbacks[i].combo); } } // if the key you pressed matches the type of sequence without // being a modifier (ie "keyup" or "keypress") then we should // reset all sequences that were not matched by this event // // this is so, for example, if you have the sequence "h a t" and you // type "h e a r t" it does not match. in this case the "e" will // cause the sequence to reset // // modifier keys are ignored because you can have a sequence // that contains modifiers such as "enter ctrl+space" and in most // cases the modifier key will be pressed before the next key // // also if you have a sequence such as "ctrl+b a" then pressing the // "b" key will trigger a "keypress" and a "keydown" // // the "keydown" is expected when there is a modifier, but the // "keypress" ends up matching the _nextExpectedAction since it occurs // after and that causes the sequence to reset // // we ignore keypresses in a sequence that directly follow a keydown // for the same character var ignoreThisKeypress = e.type == 'keypress' && _ignoreNextKeypress; if (e.type == _nextExpectedAction && !_isModifier(character) && !ignoreThisKeypress) { _resetSequences(doNotReset); } _ignoreNextKeypress = processedSequenceCallback && e.type == 'keydown'; }; /** * handles a keydown event * * @param {Event} e * @returns void */ function _handleKeyEvent(e) { // normalize e.which for key events // @see http://stackoverflow.com/questions/4285627/javascript-keycode-vs-charcode-utter-confusion if (typeof e.which !== 'number') { e.which = e.keyCode; } var character = _characterFromEvent(e); // no character found then stop if (!character) { return; } // need to use === for the character check because the character can be 0 if (e.type == 'keyup' && _ignoreNextKeyup === character) { _ignoreNextKeyup = false; return; } self.handleKey(character, _eventModifiers(e), e); } /** * called to set a 1 second timeout on the specified sequence * * this is so after each key press in the sequence you have 1 second * to press the next key before you have to start over * * @returns void */ function _resetSequenceTimer() { clearTimeout(_resetTimer); _resetTimer = setTimeout(_resetSequences, 1000); } /** * binds a key sequence to an event * * @param {string} combo - combo specified in bind call * @param {Array} keys * @param {Function} callback * @param {string=} action * @returns void */ function _bindSequence(combo, keys, callback, action) { // start off by adding a sequence level record for this combination // and setting the level to 0 _sequenceLevels[combo] = 0; /** * callback to increase the sequence level for this sequence and reset * all other sequences that were active * * @param {string} nextAction * @returns {Function} */ function _increaseSequence(nextAction) { return function() { _nextExpectedAction = nextAction; ++_sequenceLevels[combo]; _resetSequenceTimer(); }; } /** * wraps the specified callback inside of another function in order * to reset all sequence counters as soon as this sequence is done * * @param {Event} e * @returns void */ function _callbackAndReset(e) { _fireCallback(callback, e, combo); // we should ignore the next key up if the action is key down // or keypress. this is so if you finish a sequence and // release the key the final key will not trigger a keyup if (action !== 'keyup') { _ignoreNextKeyup = _characterFromEvent(e); } // weird race condition if a sequence ends with the key // another sequence begins with setTimeout(_resetSequences, 10); } // loop through keys one at a time and bind the appropriate callback // function. for any key leading up to the final one it should // increase the sequence. after the final, it should reset all sequences // // if an action is specified in the original bind call then that will // be used throughout. otherwise we will pass the action that the // next key in the sequence should match. this allows a sequence // to mix and match keypress and keydown events depending on which // ones are better suited to the key provided for (var i = 0; i < keys.length; ++i) { var isFinal = i + 1 === keys.length; var wrappedCallback = isFinal ? _callbackAndReset : _increaseSequence(action || _getKeyInfo(keys[i + 1]).action); _bindSingle(keys[i], wrappedCallback, action, combo, i); } } /** * binds a single keyboard combination * * @param {string} combination * @param {Function} callback * @param {string=} action * @param {string=} sequenceName - name of sequence if part of sequence * @param {number=} level - what part of the sequence the command is * @returns void */ function _bindSingle(combination, callback, action, sequenceName, level) { // store a direct mapped reference for use with Mousetrap.trigger self._directMap[combination + ':' + action] = callback; // make sure multiple spaces in a row become a single space combination = combination.replace(/\s+/g, ' '); var sequence = combination.split(' '); var info; // if this pattern is a sequence of keys then run through this method // to reprocess each pattern one key at a time if (sequence.length > 1) { _bindSequence(combination, sequence, callback, action); return; } info = _getKeyInfo(combination, action); // make sure to initialize array if this is the first time // a callback is added for this key self._callbacks[info.key] = self._callbacks[info.key] || []; // remove an existing match if there is one _getMatches(info.key, info.modifiers, {type: info.action}, sequenceName, combination, level); // add this call back to the array // if it is a sequence put it at the beginning // if not put it at the end // // this is important because the way these are processed expects // the sequence ones to come first self._callbacks[info.key][sequenceName ? 'unshift' : 'push']({ callback: callback, modifiers: info.modifiers, action: info.action, seq: sequenceName, level: level, combo: combination }); } /** * binds multiple combinations to the same callback * * @param {Array} combinations * @param {Function} callback * @param {string|undefined} action * @returns void */ self._bindMultiple = function(combinations, callback, action) { for (var i = 0; i < combinations.length; ++i) { _bindSingle(combinations[i], callback, action); } }; // start! _addEvent(targetElement, 'keypress', _handleKeyEvent); _addEvent(targetElement, 'keydown', _handleKeyEvent); _addEvent(targetElement, 'keyup', _handleKeyEvent); } /** * binds an event to mousetrap * * can be a single key, a combination of keys separated with +, * an array of keys, or a sequence of keys separated by spaces * * be sure to list the modifier keys first to make sure that the * correct key ends up getting bound (the last key in the pattern) * * @param {string|Array} keys * @param {Function} callback * @param {string=} action - 'keypress', 'keydown', or 'keyup' * @returns void */ Mousetrap.prototype.bind = function(keys, callback, action) { var self = this; keys = keys instanceof Array ? keys : [keys]; self._bindMultiple.call(self, keys, callback, action); return self; }; /** * unbinds an event to mousetrap * * the unbinding sets the callback function of the specified key combo * to an empty function and deletes the corresponding key in the * _directMap dict. * * TODO: actually remove this from the _callbacks dictionary instead * of binding an empty function * * the keycombo+action has to be exactly the same as * it was defined in the bind method * * @param {string|Array} keys * @param {string} action * @returns void */ Mousetrap.prototype.unbind = function(keys, action) { var self = this; return self.bind.call(self, keys, function() {}, action); }; /** * triggers an event that has already been bound * * @param {string} keys * @param {string=} action * @returns void */ Mousetrap.prototype.trigger = function(keys, action) { var self = this; if (self._directMap[keys + ':' + action]) { self._directMap[keys + ':' + action]({}, keys); } return self; }; /** * resets the library back to its initial state. this is useful * if you want to clear out the current keyboard shortcuts and bind * new ones - for example if you switch to another page * * @returns void */ Mousetrap.prototype.reset = function() { var self = this; self._callbacks = {}; self._directMap = {}; return self; }; /** * should we stop this event before firing off callbacks * * @param {Event} e * @param {Element} element * @return {boolean} */ Mousetrap.prototype.stopCallback = function(e, element) { var self = this; // if the element has the class "mousetrap" then no need to stop if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) { return false; } if (_belongsTo(element, self.target)) { return false; } // Events originating from a shadow DOM are re-targetted and `e.target` is the shadow host, // not the initial event target in the shadow tree. Note that not all events cross the // shadow boundary. // For shadow trees with `mode: 'open'`, the initial event target is the first element in // the event’s composed path. For shadow trees with `mode: 'closed'`, the initial event // target cannot be obtained. if ('composedPath' in e && typeof e.composedPath === 'function') { // For open shadow trees, update `element` so that the following check works. var initialEventTarget = e.composedPath()[0]; if (initialEventTarget !== e.target) { element = initialEventTarget; } } // stop for input, select, and textarea return element.tagName == 'INPUT' || element.tagName == 'SELECT' || element.tagName == 'TEXTAREA' || element.isContentEditable; }; /** * exposes _handleKey publicly so it can be overwritten by extensions */ Mousetrap.prototype.handleKey = function() { var self = this; return self._handleKey.apply(self, arguments); }; /** * allow custom key mappings */ Mousetrap.addKeycodes = function(object) { for (var key in object) { if (object.hasOwnProperty(key)) { _MAP[key] = object[key]; } } _REVERSE_MAP = null; }; /** * Init the global mousetrap functions * * This method is needed to allow the global mousetrap functions to work * now that mousetrap is a constructor function. */ Mousetrap.init = function() { var documentMousetrap = Mousetrap(document); for (var method in documentMousetrap) { if (method.charAt(0) !== '_') { Mousetrap[method] = (function(method) { return function() { return documentMousetrap[method].apply(documentMousetrap, arguments); }; } (method)); } } }; Mousetrap.init(); // expose mousetrap to the global object window.Mousetrap = Mousetrap; // expose as a common js module if ( module.exports) { module.exports = Mousetrap; } // expose mousetrap as an AMD module if (typeof undefined$1 === 'function' && undefined$1.amd) { undefined$1(function() { return Mousetrap; }); } }) (typeof window !== 'undefined' ? window : null, typeof window !== 'undefined' ? document : null); }); var simple = createCommonjsModule(function (module, exports) { // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE (function(mod) { mod(codemirror); })(function(CodeMirror) { CodeMirror.defineSimpleMode = function(name, states) { CodeMirror.defineMode(name, function(config) { return CodeMirror.simpleMode(config, states); }); }; CodeMirror.simpleMode = function(config, states) { ensureState(states, "start"); var states_ = {}, meta = states.meta || {}, hasIndentation = false; for (var state in states) if (state != meta && states.hasOwnProperty(state)) { var list = states_[state] = [], orig = states[state]; for (var i = 0; i < orig.length; i++) { var data = orig[i]; list.push(new Rule(data, states)); if (data.indent || data.dedent) hasIndentation = true; } } var mode = { startState: function() { return {state: "start", pending: null, local: null, localState: null, indent: hasIndentation ? [] : null}; }, copyState: function(state) { var s = {state: state.state, pending: state.pending, local: state.local, localState: null, indent: state.indent && state.indent.slice(0)}; if (state.localState) s.localState = CodeMirror.copyState(state.local.mode, state.localState); if (state.stack) s.stack = state.stack.slice(0); for (var pers = state.persistentStates; pers; pers = pers.next) s.persistentStates = {mode: pers.mode, spec: pers.spec, state: pers.state == state.localState ? s.localState : CodeMirror.copyState(pers.mode, pers.state), next: s.persistentStates}; return s; }, token: tokenFunction(states_, config), innerMode: function(state) { return state.local && {mode: state.local.mode, state: state.localState}; }, indent: indentFunction(states_, meta) }; if (meta) for (var prop in meta) if (meta.hasOwnProperty(prop)) mode[prop] = meta[prop]; return mode; }; function ensureState(states, name) { if (!states.hasOwnProperty(name)) throw new Error("Undefined state " + name + " in simple mode"); } function toRegex(val, caret) { if (!val) return /(?:)/; var flags = ""; if (val instanceof RegExp) { if (val.ignoreCase) flags = "i"; val = val.source; } else { val = String(val); } return new RegExp((caret === false ? "" : "^") + "(?:" + val + ")", flags); } function asToken(val) { if (!val) return null; if (val.apply) return val if (typeof val == "string") return val.replace(/\./g, " "); var result = []; for (var i = 0; i < val.length; i++) result.push(val[i] && val[i].replace(/\./g, " ")); return result; } function Rule(data, states) { if (data.next || data.push) ensureState(states, data.next || data.push); this.regex = toRegex(data.regex); this.token = asToken(data.token); this.data = data; } function tokenFunction(states, config) { return function(stream, state) { if (state.pending) { var pend = state.pending.shift(); if (state.pending.length == 0) state.pending = null; stream.pos += pend.text.length; return pend.token; } if (state.local) { if (state.local.end && stream.match(state.local.end)) { var tok = state.local.endToken || null; state.local = state.localState = null; return tok; } else { var tok = state.local.mode.token(stream, state.localState), m; if (state.local.endScan && (m = state.local.endScan.exec(stream.current()))) stream.pos = stream.start + m.index; return tok; } } var curState = states[state.state]; for (var i = 0; i < curState.length; i++) { var rule = curState[i]; var matches = (!rule.data.sol || stream.sol()) && stream.match(rule.regex); if (matches) { if (rule.data.next) { state.state = rule.data.next; } else if (rule.data.push) { (state.stack || (state.stack = [])).push(state.state); state.state = rule.data.push; } else if (rule.data.pop && state.stack && state.stack.length) { state.state = state.stack.pop(); } if (rule.data.mode) enterLocalMode(config, state, rule.data.mode, rule.token); if (rule.data.indent) state.indent.push(stream.indentation() + config.indentUnit); if (rule.data.dedent) state.indent.pop(); var token = rule.token; if (token && token.apply) token = token(matches); if (matches.length > 2 && rule.token && typeof rule.token != "string") { state.pending = []; for (var j = 2; j < matches.length; j++) if (matches[j]) state.pending.push({text: matches[j], token: rule.token[j - 1]}); stream.backUp(matches[0].length - (matches[1] ? matches[1].length : 0)); return token[0]; } else if (token && token.join) { return token[0]; } else { return token; } } } stream.next(); return null; }; } function cmp(a, b) { if (a === b) return true; if (!a || typeof a != "object" || !b || typeof b != "object") return false; var props = 0; for (var prop in a) if (a.hasOwnProperty(prop)) { if (!b.hasOwnProperty(prop) || !cmp(a[prop], b[prop])) return false; props++; } for (var prop in b) if (b.hasOwnProperty(prop)) props--; return props == 0; } function enterLocalMode(config, state, spec, token) { var pers; if (spec.persistent) for (var p = state.persistentStates; p && !pers; p = p.next) if (spec.spec ? cmp(spec.spec, p.spec) : spec.mode == p.mode) pers = p; var mode = pers ? pers.mode : spec.mode || CodeMirror.getMode(config, spec.spec); var lState = pers ? pers.state : CodeMirror.startState(mode); if (spec.persistent && !pers) state.persistentStates = {mode: mode, spec: spec.spec, state: lState, next: state.persistentStates}; state.localState = lState; state.local = {mode: mode, end: spec.end && toRegex(spec.end), endScan: spec.end && spec.forceEnd !== false && toRegex(spec.end, false), endToken: token && token.join ? token[token.length - 1] : token}; } function indexOf(val, arr) { for (var i = 0; i < arr.length; i++) if (arr[i] === val) return true; } function indentFunction(states, meta) { return function(state, textAfter, line) { if (state.local && state.local.mode.indent) return state.local.mode.indent(state.localState, textAfter, line); if (state.indent == null || state.local || meta.dontIndentStates && indexOf(state.state, meta.dontIndentStates) > -1) return CodeMirror.Pass; var pos = state.indent.length - 1, rules = states[state.state]; scan: for (;;) { for (var i = 0; i < rules.length; i++) { var rule = rules[i]; if (rule.data.dedent && rule.data.dedentIfLineStart !== false) { var m = rule.regex.exec(textAfter); if (m && m[0]) { pos--; if (rule.next || rule.push) rules = states[rule.next || rule.push]; textAfter = textAfter.slice(m[0].length); continue scan; } } } break; } return pos < 0 ? 0 : state.indent[pos]; }; } }); }); var dialog = createCommonjsModule(function (module, exports) { // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE // Open simple dialogs on top of an editor. Relies on dialog.css. (function(mod) { mod(codemirror); })(function(CodeMirror) { function dialogDiv(cm, template, bottom) { var wrap = cm.getWrapperElement(); var dialog; dialog = wrap.appendChild(document.createElement("div")); if (bottom) dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom"; else dialog.className = "CodeMirror-dialog CodeMirror-dialog-top"; if (typeof template == "string") { dialog.innerHTML = template; } else { // Assuming it's a detached DOM element. dialog.appendChild(template); } CodeMirror.addClass(wrap, 'dialog-opened'); return dialog; } function closeNotification(cm, newVal) { if (cm.state.currentNotificationClose) cm.state.currentNotificationClose(); cm.state.currentNotificationClose = newVal; } CodeMirror.defineExtension("openDialog", function(template, callback, options) { if (!options) options = {}; closeNotification(this, null); var dialog = dialogDiv(this, template, options.bottom); var closed = false, me = this; function close(newVal) { if (typeof newVal == 'string') { inp.value = newVal; } else { if (closed) return; closed = true; CodeMirror.rmClass(dialog.parentNode, 'dialog-opened'); dialog.parentNode.removeChild(dialog); me.focus(); if (options.onClose) options.onClose(dialog); } } var inp = dialog.getElementsByTagName("input")[0], button; if (inp) { inp.focus(); if (options.value) { inp.value = options.value; if (options.selectValueOnOpen !== false) { inp.select(); } } if (options.onInput) CodeMirror.on(inp, "input", function(e) { options.onInput(e, inp.value, close);}); if (options.onKeyUp) CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);}); CodeMirror.on(inp, "keydown", function(e) { if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; } if (e.keyCode == 27 || (options.closeOnEnter !== false && e.keyCode == 13)) { inp.blur(); CodeMirror.e_stop(e); close(); } if (e.keyCode == 13) callback(inp.value, e); }); if (options.closeOnBlur !== false) CodeMirror.on(inp, "blur", close); } else if (button = dialog.getElementsByTagName("button")[0]) { CodeMirror.on(button, "click", function() { close(); me.focus(); }); if (options.closeOnBlur !== false) CodeMirror.on(button, "blur", close); button.focus(); } return close; }); CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) { closeNotification(this, null); var dialog = dialogDiv(this, template, options && options.bottom); var buttons = dialog.getElementsByTagName("button"); var closed = false, me = this, blurring = 1; function close() { if (closed) return; closed = true; CodeMirror.rmClass(dialog.parentNode, 'dialog-opened'); dialog.parentNode.removeChild(dialog); me.focus(); } buttons[0].focus(); for (var i = 0; i < buttons.length; ++i) { var b = buttons[i]; (function(callback) { CodeMirror.on(b, "click", function(e) { CodeMirror.e_preventDefault(e); close(); if (callback) callback(me); }); })(callbacks[i]); CodeMirror.on(b, "blur", function() { --blurring; setTimeout(function() { if (blurring <= 0) close(); }, 200); }); CodeMirror.on(b, "focus", function() { ++blurring; }); } }); /* * openNotification * Opens a notification, that can be closed with an optional timer * (default 5000ms timer) and always closes on click. * * If a notification is opened while another is opened, it will close the * currently opened one and open the new one immediately. */ CodeMirror.defineExtension("openNotification", function(template, options) { closeNotification(this, close); var dialog = dialogDiv(this, template, options && options.bottom); var closed = false, doneTimer; var duration = options && typeof options.duration !== "undefined" ? options.duration : 5000; function close() { if (closed) return; closed = true; clearTimeout(doneTimer); CodeMirror.rmClass(dialog.parentNode, 'dialog-opened'); dialog.parentNode.removeChild(dialog); } CodeMirror.on(dialog, 'click', function(e) { CodeMirror.e_preventDefault(e); close(); }); if (duration) doneTimer = setTimeout(close, duration); return close; }); }); }); var searchcursor = createCommonjsModule(function (module, exports) { // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE (function(mod) { mod(codemirror); })(function(CodeMirror) { var Pos = CodeMirror.Pos; function regexpFlags(regexp) { var flags = regexp.flags; return flags != null ? flags : (regexp.ignoreCase ? "i" : "") + (regexp.global ? "g" : "") + (regexp.multiline ? "m" : "") } function ensureFlags(regexp, flags) { var current = regexpFlags(regexp), target = current; for (var i = 0; i < flags.length; i++) if (target.indexOf(flags.charAt(i)) == -1) target += flags.charAt(i); return current == target ? regexp : new RegExp(regexp.source, target) } function maybeMultiline(regexp) { return /\\s|\\n|\n|\\W|\\D|\[\^/.test(regexp.source) } function searchRegexpForward(doc, regexp, start) { regexp = ensureFlags(regexp, "g"); for (var line = start.line, ch = start.ch, last = doc.lastLine(); line <= last; line++, ch = 0) { regexp.lastIndex = ch; var string = doc.getLine(line), match = regexp.exec(string); if (match) return {from: Pos(line, match.index), to: Pos(line, match.index + match[0].length), match: match} } } function searchRegexpForwardMultiline(doc, regexp, start) { if (!maybeMultiline(regexp)) return searchRegexpForward(doc, regexp, start) regexp = ensureFlags(regexp, "gm"); var string, chunk = 1; for (var line = start.line, last = doc.lastLine(); line <= last;) { // This grows the search buffer in exponentially-sized chunks // between matches, so that nearby matches are fast and don't // require concatenating the whole document (in case we're // searching for something that has tons of matches), but at the // same time, the amount of retries is limited. for (var i = 0; i < chunk; i++) { if (line > last) break var curLine = doc.getLine(line++); string = string == null ? curLine : string + "\n" + curLine; } chunk = chunk * 2; regexp.lastIndex = start.ch; var match = regexp.exec(string); if (match) { var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n"); var startLine = start.line + before.length - 1, startCh = before[before.length - 1].length; return {from: Pos(startLine, startCh), to: Pos(startLine + inside.length - 1, inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length), match: match} } } } function lastMatchIn(string, regexp, endMargin) { var match, from = 0; while (from <= string.length) { regexp.lastIndex = from; var newMatch = regexp.exec(string); if (!newMatch) break var end = newMatch.index + newMatch[0].length; if (end > string.length - endMargin) break if (!match || end > match.index + match[0].length) match = newMatch; from = newMatch.index + 1; } return match } function searchRegexpBackward(doc, regexp, start) { regexp = ensureFlags(regexp, "g"); for (var line = start.line, ch = start.ch, first = doc.firstLine(); line >= first; line--, ch = -1) { var string = doc.getLine(line); var match = lastMatchIn(string, regexp, ch < 0 ? 0 : string.length - ch); if (match) return {from: Pos(line, match.index), to: Pos(line, match.index + match[0].length), match: match} } } function searchRegexpBackwardMultiline(doc, regexp, start) { if (!maybeMultiline(regexp)) return searchRegexpBackward(doc, regexp, start) regexp = ensureFlags(regexp, "gm"); var string, chunkSize = 1, endMargin = doc.getLine(start.line).length - start.ch; for (var line = start.line, first = doc.firstLine(); line >= first;) { for (var i = 0; i < chunkSize && line >= first; i++) { var curLine = doc.getLine(line--); string = string == null ? curLine : curLine + "\n" + string; } chunkSize *= 2; var match = lastMatchIn(string, regexp, endMargin); if (match) { var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n"); var startLine = line + before.length, startCh = before[before.length - 1].length; return {from: Pos(startLine, startCh), to: Pos(startLine + inside.length - 1, inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length), match: match} } } } var doFold, noFold; if (String.prototype.normalize) { doFold = function(str) { return str.normalize("NFD").toLowerCase() }; noFold = function(str) { return str.normalize("NFD") }; } else { doFold = function(str) { return str.toLowerCase() }; noFold = function(str) { return str }; } // Maps a position in a case-folded line back to a position in the original line // (compensating for codepoints increasing in number during folding) function adjustPos(orig, folded, pos, foldFunc) { if (orig.length == folded.length) return pos for (var min = 0, max = pos + Math.max(0, orig.length - folded.length);;) { if (min == max) return min var mid = (min + max) >> 1; var len = foldFunc(orig.slice(0, mid)).length; if (len == pos) return mid else if (len > pos) max = mid; else min = mid + 1; } } function searchStringForward(doc, query, start, caseFold) { // Empty string would match anything and never progress, so we // define it to match nothing instead. if (!query.length) return null var fold = caseFold ? doFold : noFold; var lines = fold(query).split(/\r|\n\r?/); search: for (var line = start.line, ch = start.ch, last = doc.lastLine() + 1 - lines.length; line <= last; line++, ch = 0) { var orig = doc.getLine(line).slice(ch), string = fold(orig); if (lines.length == 1) { var found = string.indexOf(lines[0]); if (found == -1) continue search var start = adjustPos(orig, string, found, fold) + ch; return {from: Pos(line, adjustPos(orig, string, found, fold) + ch), to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold) + ch)} } else { var cutFrom = string.length - lines[0].length; if (string.slice(cutFrom) != lines[0]) continue search for (var i = 1; i < lines.length - 1; i++) if (fold(doc.getLine(line + i)) != lines[i]) continue search var end = doc.getLine(line + lines.length - 1), endString = fold(end), lastLine = lines[lines.length - 1]; if (endString.slice(0, lastLine.length) != lastLine) continue search return {from: Pos(line, adjustPos(orig, string, cutFrom, fold) + ch), to: Pos(line + lines.length - 1, adjustPos(end, endString, lastLine.length, fold))} } } } function searchStringBackward(doc, query, start, caseFold) { if (!query.length) return null var fold = caseFold ? doFold : noFold; var lines = fold(query).split(/\r|\n\r?/); search: for (var line = start.line, ch = start.ch, first = doc.firstLine() - 1 + lines.length; line >= first; line--, ch = -1) { var orig = doc.getLine(line); if (ch > -1) orig = orig.slice(0, ch); var string = fold(orig); if (lines.length == 1) { var found = string.lastIndexOf(lines[0]); if (found == -1) continue search return {from: Pos(line, adjustPos(orig, string, found, fold)), to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold))} } else { var lastLine = lines[lines.length - 1]; if (string.slice(0, lastLine.length) != lastLine) continue search for (var i = 1, start = line - lines.length + 1; i < lines.length - 1; i++) if (fold(doc.getLine(start + i)) != lines[i]) continue search var top = doc.getLine(line + 1 - lines.length), topString = fold(top); if (topString.slice(topString.length - lines[0].length) != lines[0]) continue search return {from: Pos(line + 1 - lines.length, adjustPos(top, topString, top.length - lines[0].length, fold)), to: Pos(line, adjustPos(orig, string, lastLine.length, fold))} } } } function SearchCursor(doc, query, pos, options) { this.atOccurrence = false; this.doc = doc; pos = pos ? doc.clipPos(pos) : Pos(0, 0); this.pos = {from: pos, to: pos}; var caseFold; if (typeof options == "object") { caseFold = options.caseFold; } else { // Backwards compat for when caseFold was the 4th argument caseFold = options; options = null; } if (typeof query == "string") { if (caseFold == null) caseFold = false; this.matches = function(reverse, pos) { return (reverse ? searchStringBackward : searchStringForward)(doc, query, pos, caseFold) }; } else { query = ensureFlags(query, "gm"); if (!options || options.multiline !== false) this.matches = function(reverse, pos) { return (reverse ? searchRegexpBackwardMultiline : searchRegexpForwardMultiline)(doc, query, pos) }; else this.matches = function(reverse, pos) { return (reverse ? searchRegexpBackward : searchRegexpForward)(doc, query, pos) }; } } SearchCursor.prototype = { findNext: function() {return this.find(false)}, findPrevious: function() {return this.find(true)}, find: function(reverse) { var result = this.matches(reverse, this.doc.clipPos(reverse ? this.pos.from : this.pos.to)); // Implements weird auto-growing behavior on null-matches for // backwards-compatiblity with the vim code (unfortunately) while (result && CodeMirror.cmpPos(result.from, result.to) == 0) { if (reverse) { if (result.from.ch) result.from = Pos(result.from.line, result.from.ch - 1); else if (result.from.line == this.doc.firstLine()) result = null; else result = this.matches(reverse, this.doc.clipPos(Pos(result.from.line - 1))); } else { if (result.to.ch < this.doc.getLine(result.to.line).length) result.to = Pos(result.to.line, result.to.ch + 1); else if (result.to.line == this.doc.lastLine()) result = null; else result = this.matches(reverse, Pos(result.to.line + 1, 0)); } } if (result) { this.pos = result; this.atOccurrence = true; return this.pos.match || true } else { var end = Pos(reverse ? this.doc.firstLine() : this.doc.lastLine() + 1, 0); this.pos = {from: end, to: end}; return this.atOccurrence = false } }, from: function() {if (this.atOccurrence) return this.pos.from}, to: function() {if (this.atOccurrence) return this.pos.to}, replace: function(newText, origin) { if (!this.atOccurrence) return var lines = CodeMirror.splitLines(newText); this.doc.replaceRange(lines, this.pos.from, this.pos.to, origin); this.pos.to = Pos(this.pos.from.line + lines.length - 1, lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0)); } }; CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) { return new SearchCursor(this.doc, query, pos, caseFold) }); CodeMirror.defineDocExtension("getSearchCursor", function(query, pos, caseFold) { return new SearchCursor(this, query, pos, caseFold) }); CodeMirror.defineExtension("selectMatches", function(query, caseFold) { var ranges = []; var cur = this.getSearchCursor(query, this.getCursor("from"), caseFold); while (cur.findNext()) { if (CodeMirror.cmpPos(cur.to(), this.getCursor("to")) > 0) break ranges.push({anchor: cur.from(), head: cur.to()}); } if (ranges.length) this.setSelections(ranges, 0); }); }); }); var search = createCommonjsModule(function (module, exports) { // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE // Define search commands. Depends on dialog.js or another // implementation of the openDialog method. // Replace works a little oddly -- it will do the replace on the next // Ctrl-G (or whatever is bound to findNext) press. You prevent a // replace by making sure the match is no longer selected when hitting // Ctrl-G. (function(mod) { mod(codemirror, searchcursor, dialog); })(function(CodeMirror) { function searchOverlay(query, caseInsensitive) { if (typeof query == "string") query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), caseInsensitive ? "gi" : "g"); else if (!query.global) query = new RegExp(query.source, query.ignoreCase ? "gi" : "g"); return {token: function(stream) { query.lastIndex = stream.pos; var match = query.exec(stream.string); if (match && match.index == stream.pos) { stream.pos += match[0].length || 1; return "searching"; } else if (match) { stream.pos = match.index; } else { stream.skipToEnd(); } }}; } function SearchState() { this.posFrom = this.posTo = this.lastQuery = this.query = null; this.overlay = null; } function getSearchState(cm) { return cm.state.search || (cm.state.search = new SearchState()); } function queryCaseInsensitive(query) { return typeof query == "string" && query == query.toLowerCase(); } function getSearchCursor(cm, query, pos) { // Heuristic: if the query string is all lowercase, do a case insensitive search. return cm.getSearchCursor(query, pos, {caseFold: queryCaseInsensitive(query), multiline: true}); } function persistentDialog(cm, text, deflt, onEnter, onKeyDown) { cm.openDialog(text, onEnter, { value: deflt, selectValueOnOpen: true, closeOnEnter: false, onClose: function() { clearSearch(cm); }, onKeyDown: onKeyDown }); } function dialog(cm, text, shortText, deflt, f) { if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true}); else f(prompt(shortText, deflt)); } function confirmDialog(cm, text, shortText, fs) { if (cm.openConfirm) cm.openConfirm(text, fs); else if (confirm(shortText)) fs[0](); } function parseString(string) { return string.replace(/\\([nrt\\])/g, function(match, ch) { if (ch == "n") return "\n" if (ch == "r") return "\r" if (ch == "t") return "\t" if (ch == "\\") return "\\" return match }) } function parseQuery(query) { var isRE = query.match(/^\/(.*)\/([a-z]*)$/); if (isRE) { try { query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i"); } catch(e) {} // Not a regular expression after all, do a string search } else { query = parseString(query); } if (typeof query == "string" ? query == "" : query.test("")) query = /x^/; return query; } function startSearch(cm, state, query) { state.queryText = query; state.query = parseQuery(query); cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query)); state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query)); cm.addOverlay(state.overlay); if (cm.showMatchesOnScrollbar) { if (state.annotate) { state.annotate.clear(); state.annotate = null; } state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query)); } } function doSearch(cm, rev, persistent, immediate) { var state = getSearchState(cm); if (state.query) return findNext(cm, rev); var q = cm.getSelection() || state.lastQuery; if (q instanceof RegExp && q.source == "x^") q = null; if (persistent && cm.openDialog) { var hiding = null; var searchNext = function(query, event) { CodeMirror.e_stop(event); if (!query) return; if (query != state.queryText) { startSearch(cm, state, query); state.posFrom = state.posTo = cm.getCursor(); } if (hiding) hiding.style.opacity = 1; findNext(cm, event.shiftKey, function(_, to) { var dialog; if (to.line < 3 && document.querySelector && (dialog = cm.display.wrapper.querySelector(".CodeMirror-dialog")) && dialog.getBoundingClientRect().bottom - 4 > cm.cursorCoords(to, "window").top) (hiding = dialog).style.opacity = .4; }); }; persistentDialog(cm, getQueryDialog(cm), q, searchNext, function(event, query) { var keyName = CodeMirror.keyName(event); var extra = cm.getOption('extraKeys'), cmd = (extra && extra[keyName]) || CodeMirror.keyMap[cm.getOption("keyMap")][keyName]; if (cmd == "findNext" || cmd == "findPrev" || cmd == "findPersistentNext" || cmd == "findPersistentPrev") { CodeMirror.e_stop(event); startSearch(cm, getSearchState(cm), query); cm.execCommand(cmd); } else if (cmd == "find" || cmd == "findPersistent") { CodeMirror.e_stop(event); searchNext(query, event); } }); if (immediate && q) { startSearch(cm, state, q); findNext(cm, rev); } } else { dialog(cm, getQueryDialog(cm), "Search for:", q, function(query) { if (query && !state.query) cm.operation(function() { startSearch(cm, state, query); state.posFrom = state.posTo = cm.getCursor(); findNext(cm, rev); }); }); } } function findNext(cm, rev, callback) {cm.operation(function() { var state = getSearchState(cm); var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo); if (!cursor.find(rev)) { cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0)); if (!cursor.find(rev)) return; } cm.setSelection(cursor.from(), cursor.to()); cm.scrollIntoView({from: cursor.from(), to: cursor.to()}, 20); state.posFrom = cursor.from(); state.posTo = cursor.to(); if (callback) callback(cursor.from(), cursor.to()); });} function clearSearch(cm) {cm.operation(function() { var state = getSearchState(cm); state.lastQuery = state.query; if (!state.query) return; state.query = state.queryText = null; cm.removeOverlay(state.overlay); if (state.annotate) { state.annotate.clear(); state.annotate = null; } });} function getQueryDialog(cm) { return '' + cm.phrase("Search:") + ' ' + cm.phrase("(Use /re/ syntax for regexp search)") + ''; } function getReplaceQueryDialog(cm) { return ' ' + cm.phrase("(Use /re/ syntax for regexp search)") + ''; } function getReplacementQueryDialog(cm) { return '' + cm.phrase("With:") + ' '; } function getDoReplaceConfirm(cm) { return '' + cm.phrase("Replace?") + ' '; } function replaceAll(cm, query, text) { cm.operation(function() { for (var cursor = getSearchCursor(cm, query); cursor.findNext();) { if (typeof query != "string") { var match = cm.getRange(cursor.from(), cursor.to()).match(query); cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];})); } else cursor.replace(text); } }); } function replace(cm, all) { if (cm.getOption("readOnly")) return; var query = cm.getSelection() || getSearchState(cm).lastQuery; var dialogText = '' + (all ? cm.phrase("Replace all:") : cm.phrase("Replace:")) + ''; dialog(cm, dialogText + getReplaceQueryDialog(cm), dialogText, query, function(query) { if (!query) return; query = parseQuery(query); dialog(cm, getReplacementQueryDialog(cm), cm.phrase("Replace with:"), "", function(text) { text = parseString(text); if (all) { replaceAll(cm, query, text); } else { clearSearch(cm); var cursor = getSearchCursor(cm, query, cm.getCursor("from")); var advance = function() { var start = cursor.from(), match; if (!(match = cursor.findNext())) { cursor = getSearchCursor(cm, query); if (!(match = cursor.findNext()) || (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return; } cm.setSelection(cursor.from(), cursor.to()); cm.scrollIntoView({from: cursor.from(), to: cursor.to()}); confirmDialog(cm, getDoReplaceConfirm(cm), cm.phrase("Replace?"), [function() {doReplace(match);}, advance, function() {replaceAll(cm, query, text);}]); }; var doReplace = function(match) { cursor.replace(typeof query == "string" ? text : text.replace(/\$(\d)/g, function(_, i) {return match[i];})); advance(); }; advance(); } }); }); } CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);}; CodeMirror.commands.findPersistent = function(cm) {clearSearch(cm); doSearch(cm, false, true);}; CodeMirror.commands.findPersistentNext = function(cm) {doSearch(cm, false, true, true);}; CodeMirror.commands.findPersistentPrev = function(cm) {doSearch(cm, true, true, true);}; CodeMirror.commands.findNext = doSearch; CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);}; CodeMirror.commands.clearSearch = clearSearch; CodeMirror.commands.replace = replace; CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);}; }); }); var rulers = createCommonjsModule(function (module, exports) { // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE (function(mod) { mod(codemirror); })(function(CodeMirror) { CodeMirror.defineOption("rulers", false, function(cm, val) { if (cm.state.rulerDiv) { cm.state.rulerDiv.parentElement.removeChild(cm.state.rulerDiv); cm.state.rulerDiv = null; cm.off("refresh", drawRulers); } if (val && val.length) { cm.state.rulerDiv = cm.display.lineSpace.parentElement.insertBefore(document.createElement("div"), cm.display.lineSpace); cm.state.rulerDiv.className = "CodeMirror-rulers"; drawRulers(cm); cm.on("refresh", drawRulers); } }); function drawRulers(cm) { cm.state.rulerDiv.textContent = ""; var val = cm.getOption("rulers"); var cw = cm.defaultCharWidth(); var left = cm.charCoords(CodeMirror.Pos(cm.firstLine(), 0), "div").left; cm.state.rulerDiv.style.minHeight = (cm.display.scroller.offsetHeight + 30) + "px"; for (var i = 0; i < val.length; i++) { var elt = document.createElement("div"); elt.className = "CodeMirror-ruler"; var col, conf = val[i]; if (typeof conf == "number") { col = conf; } else { col = conf.column; if (conf.className) elt.className += " " + conf.className; if (conf.color) elt.style.borderColor = conf.color; if (conf.lineStyle) elt.style.borderLeftStyle = conf.lineStyle; if (conf.width) elt.style.borderLeftWidth = conf.width; } elt.style.left = (left + col * cw) + "px"; cm.state.rulerDiv.appendChild(elt); } } }); }); var trailingspace = createCommonjsModule(function (module, exports) { // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE (function(mod) { mod(codemirror); })(function(CodeMirror) { CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) { if (prev == CodeMirror.Init) prev = false; if (prev && !val) cm.removeOverlay("trailingspace"); else if (!prev && val) cm.addOverlay({ token: function(stream) { for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {} if (i > stream.pos) { stream.pos = i; return null; } stream.pos = l; return "trailingspace"; }, name: "trailingspace" }); }); }); }); var foldcode = createCommonjsModule(function (module, exports) { // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE (function(mod) { mod(codemirror); })(function(CodeMirror) { function doFold(cm, pos, options, force) { if (options && options.call) { var finder = options; options = null; } else { var finder = getOption(cm, options, "rangeFinder"); } if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0); var minSize = getOption(cm, options, "minFoldSize"); function getRange(allowFolded) { var range = finder(cm, pos); if (!range || range.to.line - range.from.line < minSize) return null; var marks = cm.findMarksAt(range.from); for (var i = 0; i < marks.length; ++i) { if (marks[i].__isFold && force !== "fold") { if (!allowFolded) return null; range.cleared = true; marks[i].clear(); } } return range; } var range = getRange(true); if (getOption(cm, options, "scanUp")) while (!range && pos.line > cm.firstLine()) { pos = CodeMirror.Pos(pos.line - 1, 0); range = getRange(false); } if (!range || range.cleared || force === "unfold") return; var myWidget = makeWidget(cm, options, range); CodeMirror.on(myWidget, "mousedown", function(e) { myRange.clear(); CodeMirror.e_preventDefault(e); }); var myRange = cm.markText(range.from, range.to, { replacedWith: myWidget, clearOnEnter: getOption(cm, options, "clearOnEnter"), __isFold: true }); myRange.on("clear", function(from, to) { CodeMirror.signal(cm, "unfold", cm, from, to); }); CodeMirror.signal(cm, "fold", cm, range.from, range.to); } function makeWidget(cm, options, range) { var widget = getOption(cm, options, "widget"); if (typeof widget == "function") { widget = widget(range.from, range.to); } if (typeof widget == "string") { var text = document.createTextNode(widget); widget = document.createElement("span"); widget.appendChild(text); widget.className = "CodeMirror-foldmarker"; } else if (widget) { widget = widget.cloneNode(true); } return widget; } // Clumsy backwards-compatible interface CodeMirror.newFoldFunction = function(rangeFinder, widget) { return function(cm, pos) { doFold(cm, pos, {rangeFinder: rangeFinder, widget: widget}); }; }; // New-style interface CodeMirror.defineExtension("foldCode", function(pos, options, force) { doFold(this, pos, options, force); }); CodeMirror.defineExtension("isFolded", function(pos) { var marks = this.findMarksAt(pos); for (var i = 0; i < marks.length; ++i) if (marks[i].__isFold) return true; }); CodeMirror.commands.toggleFold = function(cm) { cm.foldCode(cm.getCursor()); }; CodeMirror.commands.fold = function(cm) { cm.foldCode(cm.getCursor(), null, "fold"); }; CodeMirror.commands.unfold = function(cm) { cm.foldCode(cm.getCursor(), null, "unfold"); }; CodeMirror.commands.foldAll = function(cm) { cm.operation(function() { for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++) cm.foldCode(CodeMirror.Pos(i, 0), null, "fold"); }); }; CodeMirror.commands.unfoldAll = function(cm) { cm.operation(function() { for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++) cm.foldCode(CodeMirror.Pos(i, 0), null, "unfold"); }); }; CodeMirror.registerHelper("fold", "combine", function() { var funcs = Array.prototype.slice.call(arguments, 0); return function(cm, start) { for (var i = 0; i < funcs.length; ++i) { var found = funcs[i](cm, start); if (found) return found; } }; }); CodeMirror.registerHelper("fold", "auto", function(cm, start) { var helpers = cm.getHelpers(start, "fold"); for (var i = 0; i < helpers.length; i++) { var cur = helpers[i](cm, start); if (cur) return cur; } }); var defaultOptions = { rangeFinder: CodeMirror.fold.auto, widget: "\u2194", minFoldSize: 0, scanUp: false, clearOnEnter: true }; CodeMirror.defineOption("foldOptions", null); function getOption(cm, options, name) { if (options && options[name] !== undefined) return options[name]; var editorOptions = cm.options.foldOptions; if (editorOptions && editorOptions[name] !== undefined) return editorOptions[name]; return defaultOptions[name]; } CodeMirror.defineExtension("foldOption", function(options, name) { return getOption(this, options, name); }); }); }); var foldgutter = createCommonjsModule(function (module, exports) { // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE (function(mod) { mod(codemirror, foldcode); })(function(CodeMirror) { CodeMirror.defineOption("foldGutter", false, function(cm, val, old) { if (old && old != CodeMirror.Init) { cm.clearGutter(cm.state.foldGutter.options.gutter); cm.state.foldGutter = null; cm.off("gutterClick", onGutterClick); cm.off("changes", onChange); cm.off("viewportChange", onViewportChange); cm.off("fold", onFold); cm.off("unfold", onFold); cm.off("swapDoc", onChange); } if (val) { cm.state.foldGutter = new State(parseOptions(val)); updateInViewport(cm); cm.on("gutterClick", onGutterClick); cm.on("changes", onChange); cm.on("viewportChange", onViewportChange); cm.on("fold", onFold); cm.on("unfold", onFold); cm.on("swapDoc", onChange); } }); var Pos = CodeMirror.Pos; function State(options) { this.options = options; this.from = this.to = 0; } function parseOptions(opts) { if (opts === true) opts = {}; if (opts.gutter == null) opts.gutter = "CodeMirror-foldgutter"; if (opts.indicatorOpen == null) opts.indicatorOpen = "CodeMirror-foldgutter-open"; if (opts.indicatorFolded == null) opts.indicatorFolded = "CodeMirror-foldgutter-folded"; return opts; } function isFolded(cm, line) { var marks = cm.findMarks(Pos(line, 0), Pos(line + 1, 0)); for (var i = 0; i < marks.length; ++i) { if (marks[i].__isFold) { var fromPos = marks[i].find(-1); if (fromPos && fromPos.line === line) return marks[i]; } } } function marker(spec) { if (typeof spec == "string") { var elt = document.createElement("div"); elt.className = spec + " CodeMirror-guttermarker-subtle"; return elt; } else { return spec.cloneNode(true); } } function updateFoldInfo(cm, from, to) { var opts = cm.state.foldGutter.options, cur = from - 1; var minSize = cm.foldOption(opts, "minFoldSize"); var func = cm.foldOption(opts, "rangeFinder"); // we can reuse the built-in indicator element if its className matches the new state var clsFolded = typeof opts.indicatorFolded == "string" && classTest(opts.indicatorFolded); var clsOpen = typeof opts.indicatorOpen == "string" && classTest(opts.indicatorOpen); cm.eachLine(from, to, function(line) { ++cur; var mark = null; var old = line.gutterMarkers; if (old) old = old[opts.gutter]; if (isFolded(cm, cur)) { if (clsFolded && old && clsFolded.test(old.className)) return; mark = marker(opts.indicatorFolded); } else { var pos = Pos(cur, 0); var range = func && func(cm, pos); if (range && range.to.line - range.from.line >= minSize) { if (clsOpen && old && clsOpen.test(old.className)) return; mark = marker(opts.indicatorOpen); } } if (!mark && !old) return; cm.setGutterMarker(line, opts.gutter, mark); }); } // copied from CodeMirror/src/util/dom.js function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } function updateInViewport(cm) { var vp = cm.getViewport(), state = cm.state.foldGutter; if (!state) return; cm.operation(function() { updateFoldInfo(cm, vp.from, vp.to); }); state.from = vp.from; state.to = vp.to; } function onGutterClick(cm, line, gutter) { var state = cm.state.foldGutter; if (!state) return; var opts = state.options; if (gutter != opts.gutter) return; var folded = isFolded(cm, line); if (folded) folded.clear(); else cm.foldCode(Pos(line, 0), opts); } function onChange(cm) { var state = cm.state.foldGutter; if (!state) return; var opts = state.options; state.from = state.to = 0; clearTimeout(state.changeUpdate); state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, opts.foldOnChangeTimeSpan || 600); } function onViewportChange(cm) { var state = cm.state.foldGutter; if (!state) return; var opts = state.options; clearTimeout(state.changeUpdate); state.changeUpdate = setTimeout(function() { var vp = cm.getViewport(); if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) { updateInViewport(cm); } else { cm.operation(function() { if (vp.from < state.from) { updateFoldInfo(cm, vp.from, state.from); state.from = vp.from; } if (vp.to > state.to) { updateFoldInfo(cm, state.to, vp.to); state.to = vp.to; } }); } }, opts.updateViewportTimeSpan || 400); } function onFold(cm, from) { var state = cm.state.foldGutter; if (!state) return; var line = from.line; if (line >= state.from && line < state.to) updateFoldInfo(cm, line, line + 1); } }); }); var showHint = createCommonjsModule(function (module, exports) { // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE (function(mod) { mod(codemirror); })(function(CodeMirror) { var HINT_ELEMENT_CLASS = "CodeMirror-hint"; var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active"; // This is the old interface, kept around for now to stay // backwards-compatible. CodeMirror.showHint = function(cm, getHints, options) { if (!getHints) return cm.showHint(options); if (options && options.async) getHints.async = true; var newOpts = {hint: getHints}; if (options) for (var prop in options) newOpts[prop] = options[prop]; return cm.showHint(newOpts); }; CodeMirror.defineExtension("showHint", function(options) { options = parseOptions(this, this.getCursor("start"), options); var selections = this.listSelections(); if (selections.length > 1) return; // By default, don't allow completion when something is selected. // A hint function can have a `supportsSelection` property to // indicate that it can handle selections. if (this.somethingSelected()) { if (!options.hint.supportsSelection) return; // Don't try with cross-line selections for (var i = 0; i < selections.length; i++) if (selections[i].head.line != selections[i].anchor.line) return; } if (this.state.completionActive) this.state.completionActive.close(); var completion = this.state.completionActive = new Completion(this, options); if (!completion.options.hint) return; CodeMirror.signal(this, "startCompletion", this); completion.update(true); }); CodeMirror.defineExtension("closeHint", function() { if (this.state.completionActive) this.state.completionActive.close(); }); function Completion(cm, options) { this.cm = cm; this.options = options; this.widget = null; this.debounce = 0; this.tick = 0; this.startPos = this.cm.getCursor("start"); this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length; var self = this; cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); }); } var requestAnimationFrame = window.requestAnimationFrame || function(fn) { return setTimeout(fn, 1000/60); }; var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout; Completion.prototype = { close: function() { if (!this.active()) return; this.cm.state.completionActive = null; this.tick = null; this.cm.off("cursorActivity", this.activityFunc); if (this.widget && this.data) CodeMirror.signal(this.data, "close"); if (this.widget) this.widget.close(); CodeMirror.signal(this.cm, "endCompletion", this.cm); }, active: function() { return this.cm.state.completionActive == this; }, pick: function(data, i) { var completion = data.list[i]; if (completion.hint) completion.hint(this.cm, data, completion); else this.cm.replaceRange(getText(completion), completion.from || data.from, completion.to || data.to, "complete"); CodeMirror.signal(data, "pick", completion); this.close(); }, cursorActivity: function() { if (this.debounce) { cancelAnimationFrame(this.debounce); this.debounce = 0; } var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line); if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch || pos.ch < this.startPos.ch || this.cm.somethingSelected() || (!pos.ch || this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) { this.close(); } else { var self = this; this.debounce = requestAnimationFrame(function() {self.update();}); if (this.widget) this.widget.disable(); } }, update: function(first) { if (this.tick == null) return var self = this, myTick = ++this.tick; fetchHints(this.options.hint, this.cm, this.options, function(data) { if (self.tick == myTick) self.finishUpdate(data, first); }); }, finishUpdate: function(data, first) { if (this.data) CodeMirror.signal(this.data, "update"); var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle); if (this.widget) this.widget.close(); this.data = data; if (data && data.list.length) { if (picked && data.list.length == 1) { this.pick(data, 0); } else { this.widget = new Widget(this, data); CodeMirror.signal(data, "shown"); } } } }; function parseOptions(cm, pos, options) { var editor = cm.options.hintOptions; var out = {}; for (var prop in defaultOptions) out[prop] = defaultOptions[prop]; if (editor) for (var prop in editor) if (editor[prop] !== undefined) out[prop] = editor[prop]; if (options) for (var prop in options) if (options[prop] !== undefined) out[prop] = options[prop]; if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos); return out; } function getText(completion) { if (typeof completion == "string") return completion; else return completion.text; } function buildKeyMap(completion, handle) { var baseMap = { Up: function() {handle.moveFocus(-1);}, Down: function() {handle.moveFocus(1);}, PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);}, PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);}, Home: function() {handle.setFocus(0);}, End: function() {handle.setFocus(handle.length - 1);}, Enter: handle.pick, Tab: handle.pick, Esc: handle.close }; var mac = /Mac/.test(navigator.platform); if (mac) { baseMap["Ctrl-P"] = function() {handle.moveFocus(-1);}; baseMap["Ctrl-N"] = function() {handle.moveFocus(1);}; } var custom = completion.options.customKeys; var ourMap = custom ? {} : baseMap; function addBinding(key, val) { var bound; if (typeof val != "string") bound = function(cm) { return val(cm, handle); }; // This mechanism is deprecated else if (baseMap.hasOwnProperty(val)) bound = baseMap[val]; else bound = val; ourMap[key] = bound; } if (custom) for (var key in custom) if (custom.hasOwnProperty(key)) addBinding(key, custom[key]); var extra = completion.options.extraKeys; if (extra) for (var key in extra) if (extra.hasOwnProperty(key)) addBinding(key, extra[key]); return ourMap; } function getHintElement(hintsElement, el) { while (el && el != hintsElement) { if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el; el = el.parentNode; } } function Widget(completion, data) { this.completion = completion; this.data = data; this.picked = false; var widget = this, cm = completion.cm; var ownerDocument = cm.getInputField().ownerDocument; var parentWindow = ownerDocument.defaultView || ownerDocument.parentWindow; var hints = this.hints = ownerDocument.createElement("ul"); var theme = completion.cm.options.theme; hints.className = "CodeMirror-hints " + theme; this.selectedHint = data.selectedHint || 0; var completions = data.list; for (var i = 0; i < completions.length; ++i) { var elt = hints.appendChild(ownerDocument.createElement("li")), cur = completions[i]; var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS); if (cur.className != null) className = cur.className + " " + className; elt.className = className; if (cur.render) cur.render(elt, data, cur); else elt.appendChild(ownerDocument.createTextNode(cur.displayText || getText(cur))); elt.hintId = i; } var container = completion.options.container || ownerDocument.body; var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null); var left = pos.left, top = pos.bottom, below = true; var offsetLeft = 0, offsetTop = 0; if (container !== ownerDocument.body) { // We offset the cursor position because left and top are relative to the offsetParent's top left corner. var isContainerPositioned = ['absolute', 'relative', 'fixed'].indexOf(parentWindow.getComputedStyle(container).position) !== -1; var offsetParent = isContainerPositioned ? container : container.offsetParent; var offsetParentPosition = offsetParent.getBoundingClientRect(); var bodyPosition = ownerDocument.body.getBoundingClientRect(); offsetLeft = (offsetParentPosition.left - bodyPosition.left - offsetParent.scrollLeft); offsetTop = (offsetParentPosition.top - bodyPosition.top - offsetParent.scrollTop); } hints.style.left = (left - offsetLeft) + "px"; hints.style.top = (top - offsetTop) + "px"; // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. var winW = parentWindow.innerWidth || Math.max(ownerDocument.body.offsetWidth, ownerDocument.documentElement.offsetWidth); var winH = parentWindow.innerHeight || Math.max(ownerDocument.body.offsetHeight, ownerDocument.documentElement.offsetHeight); container.appendChild(hints); var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH; var scrolls = hints.scrollHeight > hints.clientHeight + 1; var startScroll = cm.getScrollInfo(); if (overlapY > 0) { var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top); if (curTop - height > 0) { // Fits above cursor hints.style.top = (top = pos.top - height - offsetTop) + "px"; below = false; } else if (height > winH) { hints.style.height = (winH - 5) + "px"; hints.style.top = (top = pos.bottom - box.top - offsetTop) + "px"; var cursor = cm.getCursor(); if (data.from.ch != cursor.ch) { pos = cm.cursorCoords(cursor); hints.style.left = (left = pos.left - offsetLeft) + "px"; box = hints.getBoundingClientRect(); } } } var overlapX = box.right - winW; if (overlapX > 0) { if (box.right - box.left > winW) { hints.style.width = (winW - 5) + "px"; overlapX -= (box.right - box.left) - winW; } hints.style.left = (left = pos.left - overlapX - offsetLeft) + "px"; } if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling) node.style.paddingRight = cm.display.nativeBarWidth + "px"; cm.addKeyMap(this.keyMap = buildKeyMap(completion, { moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); }, setFocus: function(n) { widget.changeActive(n); }, menuSize: function() { return widget.screenAmount(); }, length: completions.length, close: function() { completion.close(); }, pick: function() { widget.pick(); }, data: data })); if (completion.options.closeOnUnfocus) { var closingOnBlur; cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); }); cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); }); } cm.on("scroll", this.onScroll = function() { var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect(); var newTop = top + startScroll.top - curScroll.top; var point = newTop - (parentWindow.pageYOffset || (ownerDocument.documentElement || ownerDocument.body).scrollTop); if (!below) point += hints.offsetHeight; if (point <= editor.top || point >= editor.bottom) return completion.close(); hints.style.top = newTop + "px"; hints.style.left = (left + startScroll.left - curScroll.left) + "px"; }); CodeMirror.on(hints, "dblclick", function(e) { var t = getHintElement(hints, e.target || e.srcElement); if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();} }); CodeMirror.on(hints, "click", function(e) { var t = getHintElement(hints, e.target || e.srcElement); if (t && t.hintId != null) { widget.changeActive(t.hintId); if (completion.options.completeOnSingleClick) widget.pick(); } }); CodeMirror.on(hints, "mousedown", function() { setTimeout(function(){cm.focus();}, 20); }); this.scrollToActive(); CodeMirror.signal(data, "select", completions[this.selectedHint], hints.childNodes[this.selectedHint]); return true; } Widget.prototype = { close: function() { if (this.completion.widget != this) return; this.completion.widget = null; this.hints.parentNode.removeChild(this.hints); this.completion.cm.removeKeyMap(this.keyMap); var cm = this.completion.cm; if (this.completion.options.closeOnUnfocus) { cm.off("blur", this.onBlur); cm.off("focus", this.onFocus); } cm.off("scroll", this.onScroll); }, disable: function() { this.completion.cm.removeKeyMap(this.keyMap); var widget = this; this.keyMap = {Enter: function() { widget.picked = true; }}; this.completion.cm.addKeyMap(this.keyMap); }, pick: function() { this.completion.pick(this.data, this.selectedHint); }, changeActive: function(i, avoidWrap) { if (i >= this.data.list.length) i = avoidWrap ? this.data.list.length - 1 : 0; else if (i < 0) i = avoidWrap ? 0 : this.data.list.length - 1; if (this.selectedHint == i) return; var node = this.hints.childNodes[this.selectedHint]; if (node) node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, ""); node = this.hints.childNodes[this.selectedHint = i]; node.className += " " + ACTIVE_HINT_ELEMENT_CLASS; this.scrollToActive(); CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node); }, scrollToActive: function() { var node = this.hints.childNodes[this.selectedHint]; if (node.offsetTop < this.hints.scrollTop) this.hints.scrollTop = node.offsetTop - 3; else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight) this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3; }, screenAmount: function() { return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1; } }; function applicableHelpers(cm, helpers) { if (!cm.somethingSelected()) return helpers var result = []; for (var i = 0; i < helpers.length; i++) if (helpers[i].supportsSelection) result.push(helpers[i]); return result } function fetchHints(hint, cm, options, callback) { if (hint.async) { hint(cm, callback, options); } else { var result = hint(cm, options); if (result && result.then) result.then(callback); else callback(result); } } function resolveAutoHints(cm, pos) { var helpers = cm.getHelpers(pos, "hint"), words; if (helpers.length) { var resolved = function(cm, callback, options) { var app = applicableHelpers(cm, helpers); function run(i) { if (i == app.length) return callback(null) fetchHints(app[i], cm, options, function(result) { if (result && result.list.length > 0) callback(result); else run(i + 1); }); } run(0); }; resolved.async = true; resolved.supportsSelection = true; return resolved } else if (words = cm.getHelper(cm.getCursor(), "hintWords")) { return function(cm) { return CodeMirror.hint.fromList(cm, {words: words}) } } else if (CodeMirror.hint.anyword) { return function(cm, options) { return CodeMirror.hint.anyword(cm, options) } } else { return function() {} } } CodeMirror.registerHelper("hint", "auto", { resolve: resolveAutoHints }); CodeMirror.registerHelper("hint", "fromList", function(cm, options) { var cur = cm.getCursor(), token = cm.getTokenAt(cur); var term, from = CodeMirror.Pos(cur.line, token.start), to = cur; if (token.start < cur.ch && /\w/.test(token.string.charAt(cur.ch - token.start - 1))) { term = token.string.substr(0, cur.ch - token.start); } else { term = ""; from = cur; } var found = []; for (var i = 0; i < options.words.length; i++) { var word = options.words[i]; if (word.slice(0, term.length) == term) found.push(word); } if (found.length) return {list: found, from: from, to: to}; }); CodeMirror.commands.autocomplete = CodeMirror.showHint; var defaultOptions = { hint: CodeMirror.hint.auto, completeSingle: true, alignWithWord: true, closeCharacters: /[\s()\[\]{};:>,]/, closeOnUnfocus: true, completeOnSingleClick: true, container: null, customKeys: null, extraKeys: null }; CodeMirror.defineOption("hintOptions", null); }); }); var comment = createCommonjsModule(function (module, exports) { // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE (function(mod) { mod(codemirror); })(function(CodeMirror) { var noOptions = {}; var nonWS = /[^\s\u00a0]/; var Pos = CodeMirror.Pos; function firstNonWS(str) { var found = str.search(nonWS); return found == -1 ? 0 : found; } CodeMirror.commands.toggleComment = function(cm) { cm.toggleComment(); }; CodeMirror.defineExtension("toggleComment", function(options) { if (!options) options = noOptions; var cm = this; var minLine = Infinity, ranges = this.listSelections(), mode = null; for (var i = ranges.length - 1; i >= 0; i--) { var from = ranges[i].from(), to = ranges[i].to(); if (from.line >= minLine) continue; if (to.line >= minLine) to = Pos(minLine, 0); minLine = from.line; if (mode == null) { if (cm.uncomment(from, to, options)) mode = "un"; else { cm.lineComment(from, to, options); mode = "line"; } } else if (mode == "un") { cm.uncomment(from, to, options); } else { cm.lineComment(from, to, options); } } }); // Rough heuristic to try and detect lines that are part of multi-line string function probablyInsideString(cm, pos, line) { return /\bstring\b/.test(cm.getTokenTypeAt(Pos(pos.line, 0))) && !/^[\'\"\`]/.test(line) } function getMode(cm, pos) { var mode = cm.getMode(); return mode.useInnerComments === false || !mode.innerMode ? mode : cm.getModeAt(pos) } CodeMirror.defineExtension("lineComment", function(from, to, options) { if (!options) options = noOptions; var self = this, mode = getMode(self, from); var firstLine = self.getLine(from.line); if (firstLine == null || probablyInsideString(self, from, firstLine)) return; var commentString = options.lineComment || mode.lineComment; if (!commentString) { if (options.blockCommentStart || mode.blockCommentStart) { options.fullLines = true; self.blockComment(from, to, options); } return; } var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1); var pad = options.padding == null ? " " : options.padding; var blankLines = options.commentBlankLines || from.line == to.line; self.operation(function() { if (options.indent) { var baseString = null; for (var i = from.line; i < end; ++i) { var line = self.getLine(i); var whitespace = line.slice(0, firstNonWS(line)); if (baseString == null || baseString.length > whitespace.length) { baseString = whitespace; } } for (var i = from.line; i < end; ++i) { var line = self.getLine(i), cut = baseString.length; if (!blankLines && !nonWS.test(line)) continue; if (line.slice(0, cut) != baseString) cut = firstNonWS(line); self.replaceRange(baseString + commentString + pad, Pos(i, 0), Pos(i, cut)); } } else { for (var i = from.line; i < end; ++i) { if (blankLines || nonWS.test(self.getLine(i))) self.replaceRange(commentString + pad, Pos(i, 0)); } } }); }); CodeMirror.defineExtension("blockComment", function(from, to, options) { if (!options) options = noOptions; var self = this, mode = getMode(self, from); var startString = options.blockCommentStart || mode.blockCommentStart; var endString = options.blockCommentEnd || mode.blockCommentEnd; if (!startString || !endString) { if ((options.lineComment || mode.lineComment) && options.fullLines != false) self.lineComment(from, to, options); return; } if (/\bcomment\b/.test(self.getTokenTypeAt(Pos(from.line, 0)))) return var end = Math.min(to.line, self.lastLine()); if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end; var pad = options.padding == null ? " " : options.padding; if (from.line > end) return; self.operation(function() { if (options.fullLines != false) { var lastLineHasText = nonWS.test(self.getLine(end)); self.replaceRange(pad + endString, Pos(end)); self.replaceRange(startString + pad, Pos(from.line, 0)); var lead = options.blockCommentLead || mode.blockCommentLead; if (lead != null) for (var i = from.line + 1; i <= end; ++i) if (i != end || lastLineHasText) self.replaceRange(lead + pad, Pos(i, 0)); } else { self.replaceRange(endString, to); self.replaceRange(startString, from); } }); }); CodeMirror.defineExtension("uncomment", function(from, to, options) { if (!options) options = noOptions; var self = this, mode = getMode(self, from); var end = Math.min(to.ch != 0 || to.line == from.line ? to.line : to.line - 1, self.lastLine()), start = Math.min(from.line, end); // Try finding line comments var lineString = options.lineComment || mode.lineComment, lines = []; var pad = options.padding == null ? " " : options.padding, didSomething; lineComment: { if (!lineString) break lineComment; for (var i = start; i <= end; ++i) { var line = self.getLine(i); var found = line.indexOf(lineString); if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1; if (found == -1 && nonWS.test(line)) break lineComment; if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment; lines.push(line); } self.operation(function() { for (var i = start; i <= end; ++i) { var line = lines[i - start]; var pos = line.indexOf(lineString), endPos = pos + lineString.length; if (pos < 0) continue; if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length; didSomething = true; self.replaceRange("", Pos(i, pos), Pos(i, endPos)); } }); if (didSomething) return true; } // Try block comments var startString = options.blockCommentStart || mode.blockCommentStart; var endString = options.blockCommentEnd || mode.blockCommentEnd; if (!startString || !endString) return false; var lead = options.blockCommentLead || mode.blockCommentLead; var startLine = self.getLine(start), open = startLine.indexOf(startString); if (open == -1) return false var endLine = end == start ? startLine : self.getLine(end); var close = endLine.indexOf(endString, end == start ? open + startString.length : 0); var insideStart = Pos(start, open + 1), insideEnd = Pos(end, close + 1); if (close == -1 || !/comment/.test(self.getTokenTypeAt(insideStart)) || !/comment/.test(self.getTokenTypeAt(insideEnd)) || self.getRange(insideStart, insideEnd, "\n").indexOf(endString) > -1) return false; // Avoid killing block comments completely outside the selection. // Positions of the last startString before the start of the selection, and the first endString after it. var lastStart = startLine.lastIndexOf(startString, from.ch); var firstEnd = lastStart == -1 ? -1 : startLine.slice(0, from.ch).indexOf(endString, lastStart + startString.length); if (lastStart != -1 && firstEnd != -1 && firstEnd + endString.length != from.ch) return false; // Positions of the first endString after the end of the selection, and the last startString before it. firstEnd = endLine.indexOf(endString, to.ch); var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch); lastStart = (firstEnd == -1 || almostLastStart == -1) ? -1 : to.ch + almostLastStart; if (firstEnd != -1 && lastStart != -1 && lastStart != to.ch) return false; self.operation(function() { self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)), Pos(end, close + endString.length)); var openEnd = open + startString.length; if (pad && startLine.slice(openEnd, openEnd + pad.length) == pad) openEnd += pad.length; self.replaceRange("", Pos(start, open), Pos(start, openEnd)); if (lead) for (var i = start + 1; i <= end; ++i) { var line = self.getLine(i), found = line.indexOf(lead); if (found == -1 || nonWS.test(line.slice(0, found))) continue; var foundEnd = found + lead.length; if (pad && line.slice(foundEnd, foundEnd + pad.length) == pad) foundEnd += pad.length; self.replaceRange("", Pos(i, found), Pos(i, foundEnd)); } }); return true; }); }); }); var placeholder = createCommonjsModule(function (module, exports) { // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE (function(mod) { mod(codemirror); })(function(CodeMirror) { CodeMirror.defineOption("placeholder", "", function(cm, val, old) { var prev = old && old != CodeMirror.Init; if (val && !prev) { cm.on("blur", onBlur); cm.on("change", onChange); cm.on("swapDoc", onChange); onChange(cm); } else if (!val && prev) { cm.off("blur", onBlur); cm.off("change", onChange); cm.off("swapDoc", onChange); clearPlaceholder(cm); var wrapper = cm.getWrapperElement(); wrapper.className = wrapper.className.replace(" CodeMirror-empty", ""); } if (val && !cm.hasFocus()) onBlur(cm); }); function clearPlaceholder(cm) { if (cm.state.placeholder) { cm.state.placeholder.parentNode.removeChild(cm.state.placeholder); cm.state.placeholder = null; } } function setPlaceholder(cm) { clearPlaceholder(cm); var elt = cm.state.placeholder = document.createElement("pre"); elt.style.cssText = "height: 0; overflow: visible"; elt.style.direction = cm.getOption("direction"); elt.className = "CodeMirror-placeholder CodeMirror-line-like"; var placeHolder = cm.getOption("placeholder"); if (typeof placeHolder == "string") placeHolder = document.createTextNode(placeHolder); elt.appendChild(placeHolder); cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild); } function onBlur(cm) { if (isEmpty(cm)) setPlaceholder(cm); } function onChange(cm) { var wrapper = cm.getWrapperElement(), empty = isEmpty(cm); wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : ""); if (empty) setPlaceholder(cm); else clearPlaceholder(cm); } function isEmpty(cm) { return (cm.lineCount() === 1) && (cm.getLine(0) === ""); } }); }); var activeLine = createCommonjsModule(function (module, exports) { // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE (function(mod) { mod(codemirror); })(function(CodeMirror) { var WRAP_CLASS = "CodeMirror-activeline"; var BACK_CLASS = "CodeMirror-activeline-background"; var GUTT_CLASS = "CodeMirror-activeline-gutter"; CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) { var prev = old == CodeMirror.Init ? false : old; if (val == prev) return if (prev) { cm.off("beforeSelectionChange", selectionChange); clearActiveLines(cm); delete cm.state.activeLines; } if (val) { cm.state.activeLines = []; updateActiveLines(cm, cm.listSelections()); cm.on("beforeSelectionChange", selectionChange); } }); function clearActiveLines(cm) { for (var i = 0; i < cm.state.activeLines.length; i++) { cm.removeLineClass(cm.state.activeLines[i], "wrap", WRAP_CLASS); cm.removeLineClass(cm.state.activeLines[i], "background", BACK_CLASS); cm.removeLineClass(cm.state.activeLines[i], "gutter", GUTT_CLASS); } } function sameArray(a, b) { if (a.length != b.length) return false; for (var i = 0; i < a.length; i++) if (a[i] != b[i]) return false; return true; } function updateActiveLines(cm, ranges) { var active = []; for (var i = 0; i < ranges.length; i++) { var range = ranges[i]; var option = cm.getOption("styleActiveLine"); if (typeof option == "object" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty()) continue var line = cm.getLineHandleVisualStart(range.head.line); if (active[active.length - 1] != line) active.push(line); } if (sameArray(cm.state.activeLines, active)) return; cm.operation(function() { clearActiveLines(cm); for (var i = 0; i < active.length; i++) { cm.addLineClass(active[i], "wrap", WRAP_CLASS); cm.addLineClass(active[i], "background", BACK_CLASS); cm.addLineClass(active[i], "gutter", GUTT_CLASS); } cm.state.activeLines = active; }); } function selectionChange(cm, sel) { updateActiveLines(cm, sel.ranges); } }); }); codemirror.registerHelper("fold", "beancount", (cm, start) => { const maxDepth = 100; function headerLevel(lineNo) { const line = cm.getDoc().getLine(lineNo); const match = line && line.match(/^\*+/); if (match) { return match[0].length; } return maxDepth; } const level = headerLevel(start.line); if (level === maxDepth) { return undefined; } const doc = cm.getDoc(); const lastLineNo = doc.lastLine(); let end = start.line; while (end < lastLineNo) { if (headerLevel(end + 1) <= level) { break; } end += 1; } return { from: new codemirror.Pos(start.line, doc.getLine(start.line).length), to: new codemirror.Pos(end, doc.getLine(end).length), }; }); function getCurrentWord(cursor, line) { return line.slice(0, cursor.ch).match(/(\S*)$/)[0]; } function fuzzyMatch(cursor, currentWord, completions) { const search = currentWord.toLowerCase(); return { list: completions.filter(completion => fuzzytest(search, completion)), from: new codemirror.Pos(cursor.line, cursor.ch - currentWord.length), to: cursor, }; } const completionSources = { undatedDirectives: ["option", "plugin", "include"], datedDirectives: [ "open", "close", "commodity", "balance", "pad", "note", "document", "price", "event", "query", ], }; const directiveCompletions = { open: ["accounts", "currencies"], close: ["accounts"], commodity: ["currencies"], balance: ["accounts", null, "currencies"], pad: ["accounts", "accounts"], note: ["accounts"], document: ["accounts"], price: ["currencies", null, "currencies"], }; codemirror.registerHelper("hint", "beancount", (cm) => { const doc = cm.getDoc(); const cursor = doc.getCursor(); const line = doc.getLine(cursor.line); const token = cm.getTokenAt(cursor); const currentCharacter = line[cursor.ch - 1]; const currentWord = getCurrentWord(cursor, line); // If '#' or '^' has just been typed, there won't be a tag or link token yet if (currentCharacter === "#" || currentCharacter === "^") { const list = currentCharacter === "#" ? favaAPI.tags : favaAPI.links; return { list, from: cursor, to: cursor, }; } if (token.type === "tag" || token.type === "link") { const list = token.type === "tag" ? favaAPI.tags : favaAPI.links; return { list: list.filter(d => d.startsWith(currentWord.slice(1))), from: new codemirror.Pos(cursor.line, token.start + 1), to: new codemirror.Pos(cursor.line, token.end), }; } // directives at the start of the line if (currentWord === line && line.length > 0) { return { list: completionSources.undatedDirectives.filter(d => d.startsWith(currentWord)), from: new codemirror.Pos(cursor.line, 0), to: cursor, }; } const lineTokens = cm.getLineTokens(cursor.line); if (lineTokens.length > 0) { const startCurrentWord = cursor.ch - currentWord.length; const previousTokens = lineTokens.filter(d => d.end <= startCurrentWord); // complete accounts for indented lines if (lineTokens[0].type === "whitespace") { if (previousTokens.length === 1) { return fuzzyMatch(cursor, currentWord, favaAPI.accounts); } } // dated directives if (lineTokens[0].type === "date") { // date whitespace -> complete directives if (previousTokens.length === 2) { return { list: completionSources.datedDirectives.filter(d => d.startsWith(currentWord)), from: new codemirror.Pos(cursor.line, cursor.ch - currentWord.length), to: cursor, }; } // Ignore negative sign from previousTokens const tokenLength = previousTokens.filter(t => t.type != null).length; if (tokenLength % 2 === 0) { const directiveType = previousTokens[2].string; if (directiveType in directiveCompletions) { const complType = directiveCompletions[directiveType][tokenLength / 2 - 2]; if (complType) { return fuzzyMatch(cursor, currentWord, favaAPI[complType]); } } } } } return { list: [], }; }); /* eslint-disable no-useless-escape */ // The rules should mirror `parser/lexel.l` in beancount codemirror.defineSimpleMode("beancount", { start: [ { regex: /\*.*/, token: "comment section", sol: true, }, { regex: /[#*;].*/, token: "comment", sol: true, }, { regex: /;.*/, token: "comment", }, { regex: /(query)(\s*)("[^"]*")(\s*)(")/, token: ["directive", "", "string", "", "string"], mode: { spec: "beancount-query", end: /"/, }, }, { regex: /"(?:[^\\]|\\.)*?"/, token: "string", }, { regex: /@|@@|{|}/, token: "bracket", }, { regex: /\s+/, token: "whitespace", }, { regex: /#[A-Za-z0-9\-_\/.]+/, token: "tag", }, { regex: /[A-Z][A-Z0-9'\._\-]{0,22}[A-Z0-9]/, token: "commodity keyword", }, { regex: /TRUE|FALSE/, token: "bool atom", }, { regex: /(?:[A-Z][A-Za-z0-9\-]+)(?::[A-Z][A-Za-z0-9\-]*)+/, token: "account", }, { regex: /[*!&#?%PSTCURM]|txn/, token: "directive transaction", }, // other dated directives { regex: /balance|open|close|commodity|pad|event|custom|price|note|document/, token: "directive", }, // undated directives { regex: /pushtag|poptag|pushmeta|popmeta|option|plugin|include/, token: "directive", sol: true, }, { regex: /[0-9]{4,}[\-\/][0-9]+[\-\/][0-9]+/, token: "date", }, { regex: /(?:[0-9]+|[0-9][0-9,]+[0-9])(?:\.[0-9]*)?/, token: "number", }, { regex: /\^[A-Za-z0-9\-_\/.]+/, token: "attribute", }, { regex: /[a-z][a-za-z0-9\-_]+:/, token: "meta", }, ], }); var bqlGrammar = { columns: [ "account", "balance", "change", "cost_currency", "cost_date", "cost_label", "cost_number", "currency", "date", "day", "description", "filename", "flag", "id", "lineno", "links", "location", "month", "narration", "number", "other_accounts", "payee", "position", "posting_flag", "price", "tags", "type", "weight", "year", ], functions: [ "abs", "account_sortkey", "any_meta", "close_date", "coalesce", "commodity", "commodity_meta", "convert", "cost", "count", "currency", "currency_meta", "date", "date_add", "date_diff", "day", "entry_meta", "filter_currency", "findfirst", "first", "getitem", "getprice", "grep", "grepn", "joinstr", "last", "leaf", "length", "max", "maxwidth", "meta", "min", "month", "neg", "number", "only", "open_date", "open_meta", "parent", "possign", "quarter", "root", "safediv", "str", "subst", "sum", "today", "units", "value", "weekday", "year", "ymonth", ], keywords: [ "and", "as", "asc", "at", "balances", "by", "clear", "close", "desc", "distinct", "errors", "explain", "false", "flatten", "from", "group", "having", "in", "journal", "limit", "not", "null", "on", "open", "or", "order", "pivot", "print", "reload", "run", "select", "true", "where", ], }; const { columns, functions, keywords } = bqlGrammar; const functionCompletions = functions.map((f) => `${f}(`); const commands = ["select"]; codemirror.registerHelper("hint", "beancount-query", (cm) => { const doc = cm.getDoc(); const cursor = doc.getCursor(); const line = doc.getLine(cursor.line); const currentWord = getCurrentWord(cursor, line); // keywords at the start of the line if (currentWord === line) { return { list: commands.filter(d => d.startsWith(currentWord)), from: new codemirror.Pos(cursor.line, 0), to: cursor, }; } return fuzzyMatch(cursor, currentWord, columns.concat(functionCompletions, keywords)); }); /* eslint-disable no-useless-escape */ const { columns: columns$1, functions: functions$1, keywords: keywords$1 } = bqlGrammar; // This should match the grammar defined in Beancount (`query/query_parser.py`). codemirror.defineSimpleMode("beancount-query", { start: [ { regex: new RegExp(`(?=^|\\s)(${keywords$1.join("|")})(?=\\s|$)`, "i"), token: "keyword", }, { regex: /(\"[^\"]*\"|\'[^\']*\')/, token: "string", }, { regex: /(?:\#(?:\"[^\"]*\"|\'[^\']*\')|\d\d\d\d-\d\d-\d\d)/, token: "date", }, { regex: /[-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)/, token: "number", }, { regex: /[-+]?[0-9]+/, token: "number", }, { regex: new RegExp(`(${columns$1.join("|")})(?=\\)|\\s|,|$)`, "i"), token: "variable-2", }, { regex: new RegExp(`(${functions$1.join("|")})(?=\\()`, "i"), token: "variable-3", }, ], }); // This handles saving in both the main and the overlaid entry editors. codemirror.commands.favaSave = (cm) => { const button = cm.getOption("favaSaveButton"); if (!button) { return; } const buttonText = button.textContent; button.disabled = true; button.textContent = button.getAttribute("data-progress-content"); putAPI("source", { file_path: button.getAttribute("data-filename"), entry_hash: button.getAttribute("data-entry-hash"), source: cm.getValue(), sha256sum: cm.getTextArea().getAttribute("data-sha256sum"), }) .then(data => { cm.focus(); cm.getTextArea().setAttribute("data-sha256sum", data); e.trigger("file-modified"); // Reload the page if an entry was changed. if (button.getAttribute("data-entry-hash")) { router.reload(); closeOverlay(); } }, error => { notify(error, "error"); }) .then(() => { cm.getDoc().markClean(); button.textContent = buttonText; }); }; codemirror.commands.favaFormat = (cm) => { putAPI("format_source", { source: cm.getValue() }).then(data => { const scrollPosition = cm.getScrollInfo().top; cm.setValue(data); cm.scrollTo(null, scrollPosition); }, error => { notify(error, "error"); }); }; codemirror.commands.favaToggleComment = (cm) => { const doc = cm.getDoc(); const args = { from: doc.getCursor("start"), to: doc.getCursor("end"), options: { lineComment: ";" }, }; if (!cm.uncomment(args.from, args.to, args.options)) { cm.lineComment(args.from, args.to, args.options); } }; codemirror.commands.favaCenterCursor = (cm) => { const { top } = cm.cursorCoords(true, "local"); const height = cm.getScrollInfo().clientHeight; cm.scrollTo(null, top - height / 2); }; codemirror.commands.favaJumpToMarker = (cm) => { const doc = cm.getDoc(); const cursor = cm.getSearchCursor("FAVA-INSERT-MARKER"); if (cursor.findNext()) { cm.focus(); doc.setCursor(cursor.from()); cm.execCommand("goLineUp"); cm.execCommand("favaCenterCursor"); } else { doc.setCursor(doc.lastLine(), 0); } }; // If the given key should be ignored for autocompletion function ignoreKey(key) { switch (key) { case "ArrowDown": case "ArrowUp": case "ArrowLeft": case "ArrowRight": case "PageDown": case "PageUp": case "Home": case "End": case "Escape": case "Enter": case "Alt": case "Control": case "Meta": case "Shift": case "CapsLock": return true; default: return false; } } // Initialize read-only editors function initReadOnlyEditors() { selectAll("textarea.editor-readonly").forEach(el => { codemirror.fromTextArea(el, { mode: "beancount", readOnly: true, }); }); } const sourceEditorOptions = { mode: "beancount", indentUnit: 4, lineNumbers: true, foldGutter: true, showTrailingSpace: true, styleActiveLine: true, gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"], extraKeys: { "Ctrl-Space": "autocomplete", "Ctrl-S": "favaSave", "Cmd-S": "favaSave", "Ctrl-D": "favaFormat", "Cmd-D": "favaFormat", "Ctrl-Y": "favaToggleComment", "Cmd-Y": "favaToggleComment", Tab: (cm) => { if (cm.getDoc().somethingSelected()) { cm.execCommand("indentMore"); } else { cm.execCommand("insertSoftTab"); } }, }, }; let activeEditor = null; // Init source editor. function initSourceEditor(name) { if (favaAPI.favaOptions["currency-column"]) { sourceEditorOptions.rulers = [ { column: favaAPI.favaOptions["currency-column"] - 1, lineStyle: "dotted", }, ]; } const sourceEditorTextarea = select(name); if (!sourceEditorTextarea) { return; } const editor = codemirror.fromTextArea(sourceEditorTextarea, sourceEditorOptions); if (name === "#source-editor") { activeEditor = editor; } const saveButton = select(`${name}-submit`); editor.setOption("favaSaveButton", saveButton); editor.on("changes", (cm) => { saveButton.disabled = cm.getDoc().isClean(); }); editor.on("keyup", (cm, event) => { if (!cm.state.completionActive && !ignoreKey(event.key)) { codemirror.commands.autocomplete(cm, undefined, { completeSingle: false, }); } }); const line = parseInt(new URLSearchParams(window.location.search).get("line") || "0", 10); if (line > 0) { editor.getDoc().setCursor(line - 1, 0); editor.execCommand("favaCenterCursor"); } else { editor.execCommand("favaJumpToMarker"); } // keybindings when the focus is outside the editor mousetrap.bind(["ctrl+s", "meta+s"], event => { event.preventDefault(); editor.execCommand("favaSave"); }); mousetrap.bind(["ctrl+d", "meta+d"], event => { event.preventDefault(); editor.execCommand("favaFormat"); }); // Run editor commands with buttons in editor menu. selectAll(`${name}-form button`).forEach(button => { const command = button.getAttribute("data-command"); if (command) { button.addEventListener("click", event => { event.preventDefault(); event.stopImmediatePropagation(); editor.execCommand(command); }); } }); } e.on("page-loaded", () => { initReadOnlyEditors(); initSourceEditor("#source-editor"); }); const leaveMessage = "There are unsaved changes. Are you sure you want to leave?"; e.on("navigate", (state) => { if (activeEditor) { if (!activeEditor.getDoc().isClean()) { // eslint-disable-next-line no-alert const leave = window.confirm(leaveMessage); if (!leave) { state.interrupt = true; } else { activeEditor = null; } } else { activeEditor = null; } } }); window.addEventListener("beforeunload", event => { if (activeEditor && !activeEditor.getDoc().isClean()) { event.returnValue = leaveMessage; } }); function addFilter(value) { filters.update(fs => { if (fs.filter) { return Object.assign(Object.assign({}, fs), { filter: `${fs.filter} ${value}` }); } return Object.assign(Object.assign({}, fs), { filter: value }); }); } e.on("page-loaded", () => { const journal = select(".journal"); if (!journal) { return; } delegate(journal, "click", "li", event => { if (!event.target) { return; } const target = event.target; if (target.tagName === "A") { return; } if (target.className === "tag" || target.className === "link") { // Filter for tags and links when clicking on them. addFilter(target.innerText); } else if (target.className === "payee") { // Filter for payees when clicking on them. addFilter(`payee:"${target.innerText}"`); } else if (target.tagName === "DD") { // Filter for metadata when clicking on the value. addFilter(` ${target.previousElementSibling.innerText}"${target.innerText}"`); } else if (target.closest(".indicators")) { // Toggle postings and metadata by clicking on indicators. const entry = target.closest(".transaction"); if (entry) { entry.classList.toggle("show-postings"); } } }); // Toggle entries with buttons. selectAll("#entry-filters button").forEach(button => { button.addEventListener("click", () => { const type = button.getAttribute("data-type"); const shouldShow = button.classList.contains("inactive"); button.classList.toggle("inactive", !shouldShow); if (type === "transaction" || type === "custom" || type === "document") { selectAll(`#entry-filters .${type}-toggle`).forEach(el => { el.classList.toggle("inactive", !shouldShow); }); } journal.classList.toggle(`show-${type}`, shouldShow); // Modify get params const filterShow = []; selectAll("#entry-filters button").forEach(el => { const datatype = el.getAttribute("data-type"); if (datatype && !el.classList.contains("inactive")) { filterShow.push(datatype); } }); const url = new URL(window.location.href); url.searchParams.delete("show"); filterShow.forEach(filter => { url.searchParams.append("show", filter); }); router.navigate(url.toString(), false); }); }); }); function click(selector) { const element = select(selector); if (element && element instanceof HTMLElement) { element.click(); } } e.on("page-loaded", () => { selectAll("[data-key]").forEach(element => { const key = element.getAttribute("data-key"); if (key !== null) { mousetrap.bind(key, () => { const tag = element.tagName; if (tag === "BUTTON" || tag === "A") { element.click(); } else if (tag === "INPUT") { element.focus(); } }, "keyup"); } }); }); // Add a tooltip showing the keyboard shortcut over the target element. function showTooltip(target) { const tooltip = document.createElement("div"); tooltip.className = "keyboard-tooltip"; tooltip.innerHTML = target.getAttribute("data-key") || ""; document.body.appendChild(tooltip); const parentCoords = target.getBoundingClientRect(); // Padded 10px to the left if there is space or centered otherwise const left = parentCoords.left + Math.min((target.offsetWidth - tooltip.offsetWidth) / 2, 10); const top = parentCoords.top + (target.offsetHeight - tooltip.offsetHeight) / 2; tooltip.style.left = `${left}px`; tooltip.style.top = `${top + window.pageYOffset}px`; } // Show all keyboard shortcut tooltips. function showTooltips() { const reloadButton = select("#reload-page"); if (reloadButton) { reloadButton.classList.remove("hidden"); } selectAll("[data-key]").forEach(el => { showTooltip(el); }); } // Remove all keyboard shortcut tooltips. function removeTooltips() { const reloadButton = select("#reload-page"); if (reloadButton) { reloadButton.classList.add("hidden"); } selectAll(".keyboard-tooltip").forEach(tooltip => { tooltip.remove(); }); } e.on("page-init", () => { mousetrap.bind("?", () => { removeTooltips(); showTooltips(); once(document, "mousedown", () => { removeTooltips(); }); }); mousetrap.bind("esc", () => { closeOverlay(); removeTooltips(); }); // Charts mousetrap.bind("c", () => { const selected = select(".chart-labels .selected"); if (selected && selected.nextElementSibling) { selected.nextElementSibling.click(); } else { click(".chart-labels label:first-child"); } }); mousetrap.bind("C", () => { const selected = select(".chart-labels .selected"); if (selected && selected.previousElementSibling) { selected.previousElementSibling.click(); } else { click(".chart-labels label:last-child"); } }); }); /** * This script updates the links and error count in the sidebar as well as * toggling the sidebar on mobile. */ function initSidebar() { selectAll("aside a").forEach(el => { el.classList.remove("selected"); const href = el.getAttribute("href"); if (href && href.includes(window.location.pathname)) { el.classList.add("selected"); } }); select("aside li.error").classList.toggle("hidden", favaAPI.errors === 0); select("aside li.error span").innerHTML = `${favaAPI.errors}`; } e.on("page-init", () => { const asideButton = select("#aside-button"); asideButton.addEventListener("click", () => { select("aside").classList.toggle("active"); asideButton.classList.toggle("active"); }); }); e.on("page-loaded", () => { initSidebar(); }); e.on("file-modified", async () => { const errors = await fetchAPI("errors"); favaAPI.errors = number(errors); initSidebar(); }); /** * Sorting of tables and the journal. * * Only clicking on headers that have a data-sort attribute will have an * effect. The currently supported values for `data-sort` are: * * - 'string': Case-insensitive string comparison. * - 'num': Clean and parse to float. */ function parseNumber(num) { const cleaned = num.replace(/[^\-?0-9.]/g, ""); const n = parseFloat(cleaned); return Number.isNaN(n) ? 0 : n; } function stringComparator(A, B) { const a = A.toLowerCase(); const b = B.toLowerCase(); if (a === b) { return 0; } return a < b ? -1 : 1; } function numComparator(a, b) { return parseNumber(a) - parseNumber(b); } /** * Obtain the value to sort by for an element. */ function getValue(el) { return el.getAttribute("data-sort-value") || el.textContent || el.innerText; } /** * Generate a sort function for a given comparison type, using a getter */ function sortFunc(type, order, getter) { const comparator = type === "num" ? numComparator : stringComparator; function func(a, b) { return (order === "asc" ? 1 : -1) * comparator(getter(a), getter(b)); } return func; } /** * Sort elements contained in a given parent element. */ function sortElements(parent, elements, selector, order, type) { const sortFunction = sortFunc(type, order, (a) => getValue(selector(a))); const fragment = document.createDocumentFragment(); elements.sort(sortFunction).forEach(el => { fragment.appendChild(el); }); parent.appendChild(fragment); } /** * Obtain the sort order for the row from the row header */ function getSortOrder(headerElement) { if (!headerElement.getAttribute("data-order")) { return headerElement.getAttribute("data-sort-default") === "desc" ? "desc" : "asc"; } return headerElement.getAttribute("data-order") === "asc" ? "desc" : "asc"; } function sortableJournal(ol) { const head = select(".head", ol); if (!head) { return; } const headers = selectAll("span[data-sort]", head); headers.forEach(header => { header.addEventListener("click", () => { const order = getSortOrder(header); const type = header.getAttribute("data-sort"); const headerClass = header.classList[0]; // update sort order headers.forEach(el => { el.removeAttribute("data-order"); }); header.setAttribute("data-order", order); sortElements(ol, [].slice.call(ol.children, 1), function selector(li) { return li.querySelector(`.${headerClass}`); }, order, type); }); }); } function sortableTable(table) { const head = table.tHead; const body = table.tBodies.item(0); if (!head || !body) { return; } const headers = selectAll("th[data-sort]", head); headers.forEach(header => { header.addEventListener("click", () => { const order = getSortOrder(header); const type = header.getAttribute("data-sort"); const index = headers.indexOf(header); // update sort order headers.forEach(el => { el.removeAttribute("data-order"); }); header.setAttribute("data-order", order); sortElements(body, selectAll("tr", body), function selector(tr) { // eslint-disable-next-line return tr.cells.item(index); }, order, type); }); }); } function initSort() { selectAll("table.sortable").forEach(el => { sortableTable(el); }); selectAll("ol.journal").forEach(el => { sortableJournal(el); }); } e.on("page-loaded", () => { initSort(); }); // Account trees. e.on("page-loaded", () => { selectAll(".tree-table").forEach(table => { const expandAllLink = select(".expand-all", table); if (!expandAllLink) { return; } expandAllLink.addEventListener("click", () => { expandAllLink.classList.add("hidden"); selectAll(".toggled", table).forEach(el => { el.classList.remove("toggled"); }); }); delegate(table, "click", "span.has-children", (event) => { if (!event.target) { return; } const target = event.target; if (target.tagName === "A") { return; } const row = target.closest("li"); const willShow = row.classList.contains("toggled"); if (event.shiftKey) { selectAll("li", row).forEach(el => { el.classList.toggle("toggled", !willShow); }); } if (event.ctrlKey || event.metaKey) { selectAll("li", row).forEach(el => { el.classList.toggle("toggled", willShow); }); } row.classList.toggle("toggled"); expandAllLink.classList.toggle("hidden", !selectAll(".toggled", table).length); }); }); }); /** * Move a file, either in an import directory or a document. * @returns whether the file was moved successfully. */ async function moveDocument(filename, account, newName) { try { const msg = await fetchAPI("move", { filename, account, newName, }); notify(msg); return true; } catch (error) { notify(error, "error"); return false; } } /* javascript/AutocompleteInput.svelte generated by Svelte v3.18.2 */ function add_css() { var style = element("style"); style.id = "svelte-16b8qvp-style"; style.textContent = "span.svelte-16b8qvp{display:inline-block;position:relative}input.svelte-16b8qvp{width:100%}ul.svelte-16b8qvp{position:absolute;float:left;z-index:var(--z-index-autocomplete);overflow-x:hidden;overflow-y:auto;background-color:var(--color-background);border:1px solid var(--color-background-darkest);box-shadow:0 3px 3px var(--color-background-darker)}li.svelte-16b8qvp{min-width:8rem;padding:0 0.5em;white-space:nowrap;cursor:pointer}li.selected.svelte-16b8qvp,li.svelte-16b8qvp:hover{color:var(--color-background);background-color:var(--color-links)}li.svelte-16b8qvp span::before{position:absolute;z-index:-1;width:0.65em;height:1.2em;margin-top:0.12em;margin-left:-0.1em;content:\"\";background-color:var(--color-autocomplete-match);border-radius:2px}"; append(document.head, style); } function get_each_context(ctx, list, i) { const child_ctx = ctx.slice(); child_ctx[25] = list[i].innerHTML; child_ctx[26] = list[i].suggestion; child_ctx[28] = i; return child_ctx; } // (158:4) {#each filteredSuggestions as { innerHTML, suggestion } function create_each_block(ctx) { let li; let html_tag; let raw_value = /*innerHTML*/ ctx[25] + ""; let t; let dispose; function mousedown_handler(...args) { return /*mousedown_handler*/ ctx[24](/*suggestion*/ ctx[26], ...args); } return { c() { li = element("li"); t = space(); html_tag = new HtmlTag(raw_value, t); attr(li, "class", "svelte-16b8qvp"); toggle_class(li, "selected", /*i*/ ctx[28] === /*index*/ ctx[6]); }, m(target, anchor) { insert(target, li, anchor); html_tag.m(li); append(li, t); dispose = listen(li, "mousedown", mousedown_handler); }, p(new_ctx, dirty) { ctx = new_ctx; if (dirty & /*filteredSuggestions*/ 16 && raw_value !== (raw_value = /*innerHTML*/ ctx[25] + "")) html_tag.p(raw_value); if (dirty & /*index*/ 64) { toggle_class(li, "selected", /*i*/ ctx[28] === /*index*/ ctx[6]); } }, d(detaching) { if (detaching) detach(li); dispose(); } }; } function create_fragment(ctx) { let span; let input_1; let t; let ul; let span_class_value; let dispose; let input_1_levels = [ { name: /*name*/ ctx[1] }, { type: "text" }, { autocomplete: "off" }, { placeholder: /*placeholder*/ ctx[2] }, /*inputOptions*/ ctx[8] ]; let input_1_data = {}; for (let i = 0; i < input_1_levels.length; i += 1) { input_1_data = assign(input_1_data, input_1_levels[i]); } let each_value = /*filteredSuggestions*/ ctx[4]; let each_blocks = []; for (let i = 0; i < each_value.length; i += 1) { each_blocks[i] = create_each_block(get_each_context(ctx, each_value, i)); } return { c() { span = element("span"); input_1 = element("input"); t = space(); ul = element("ul"); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].c(); } set_attributes(input_1, input_1_data); toggle_class(input_1, "svelte-16b8qvp", true); ul.hidden = /*hidden*/ ctx[5]; attr(ul, "class", "svelte-16b8qvp"); attr(span, "class", span_class_value = "" + (null_to_empty(/*className*/ ctx[3] || "") + " svelte-16b8qvp")); }, m(target, anchor) { insert(target, span, anchor); append(span, input_1); set_input_value(input_1, /*value*/ ctx[0]); /*input_1_binding*/ ctx[20](input_1); append(span, t); append(span, ul); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].m(ul, null); } dispose = [ listen(input_1, "input", /*input_1_input_handler*/ ctx[19]), listen(input_1, "blur", /*blur_handler*/ ctx[21]), listen(input_1, "focusin", /*focusin_handler*/ ctx[22]), listen(input_1, "input", /*input_handler*/ ctx[23]), listen(input_1, "keydown", /*keydown*/ ctx[10]) ]; }, p(ctx, [dirty]) { set_attributes(input_1, get_spread_update(input_1_levels, [ dirty & /*name*/ 2 && { name: /*name*/ ctx[1] }, { type: "text" }, { autocomplete: "off" }, dirty & /*placeholder*/ 4 && { placeholder: /*placeholder*/ ctx[2] }, dirty & /*inputOptions*/ 256 && /*inputOptions*/ ctx[8] ])); if (dirty & /*value*/ 1 && input_1.value !== /*value*/ ctx[0]) { set_input_value(input_1, /*value*/ ctx[0]); } toggle_class(input_1, "svelte-16b8qvp", true); if (dirty & /*index, mousedown, filteredSuggestions*/ 592) { each_value = /*filteredSuggestions*/ ctx[4]; let i; for (i = 0; i < each_value.length; i += 1) { const child_ctx = get_each_context(ctx, each_value, i); if (each_blocks[i]) { each_blocks[i].p(child_ctx, dirty); } else { each_blocks[i] = create_each_block(child_ctx); each_blocks[i].c(); each_blocks[i].m(ul, null); } } for (; i < each_blocks.length; i += 1) { each_blocks[i].d(1); } each_blocks.length = each_value.length; } if (dirty & /*hidden*/ 32) { ul.hidden = /*hidden*/ ctx[5]; } if (dirty & /*className*/ 8 && span_class_value !== (span_class_value = "" + (null_to_empty(/*className*/ ctx[3] || "") + " svelte-16b8qvp"))) { attr(span, "class", span_class_value); } }, i: noop, o: noop, d(detaching) { if (detaching) detach(span); /*input_1_binding*/ ctx[20](null); destroy_each(each_blocks, detaching); run_all(dispose); } }; } function instance($$self, $$props, $$invalidate) { const dispatch = createEventDispatcher(); let { value = "" } = $$props; let { suggestions = [] } = $$props; let { name = "" } = $$props; let { placeholder = "" } = $$props; let { valueExtractor } = $$props; let { valueSelector } = $$props; let { setSize = false } = $$props; let { className } = $$props; let filteredSuggestions = []; let hidden = true; let index = -1; let input; const inputOptions = {}; function focus() { input.focus(); } function setCustomValidity(str) { input.setCustomValidity(str); } function select(suggestion) { if (input && valueSelector) { $$invalidate(0, value = valueSelector(suggestion, input)); } else { $$invalidate(0, value = suggestion); } dispatch("select"); $$invalidate(5, hidden = true); } function mousedown(event, suggestion) { if (event.button === 0) { select(suggestion); } } function keydown(event) { if (event.keyCode === 13) { // ENTER if (index > -1) { event.preventDefault(); select(filteredSuggestions[index].suggestion); } } else if (event.keyCode === 27) { $$invalidate(5, hidden = true); } else if (event.keyCode === 38) { // UP event.preventDefault(); // ESC $$invalidate(6, index = index === 0 ? filteredSuggestions.length - 1 : index - 1); } else if (event.keyCode === 40) { // DOWN event.preventDefault(); $$invalidate(6, index = index === filteredSuggestions.length - 1 ? 0 : index + 1); } } function input_1_input_handler() { value = this.value; $$invalidate(0, value); } function input_1_binding($$value) { binding_callbacks[$$value ? "unshift" : "push"](() => { $$invalidate(7, input = $$value); }); } const blur_handler = () => { $$invalidate(5, hidden = true); }; const focusin_handler = () => { $$invalidate(5, hidden = false); }; const input_handler = () => { $$invalidate(5, hidden = false); }; const mousedown_handler = (suggestion, ev) => mousedown(ev, suggestion); $$self.$set = $$props => { if ("value" in $$props) $$invalidate(0, value = $$props.value); if ("suggestions" in $$props) $$invalidate(11, suggestions = $$props.suggestions); if ("name" in $$props) $$invalidate(1, name = $$props.name); if ("placeholder" in $$props) $$invalidate(2, placeholder = $$props.placeholder); if ("valueExtractor" in $$props) $$invalidate(12, valueExtractor = $$props.valueExtractor); if ("valueSelector" in $$props) $$invalidate(13, valueSelector = $$props.valueSelector); if ("setSize" in $$props) $$invalidate(14, setSize = $$props.setSize); if ("className" in $$props) $$invalidate(3, className = $$props.className); }; $$self.$$.update = () => { if ($$self.$$.dirty & /*input, valueExtractor, value, suggestions*/ 6273) { { const val = input && valueExtractor ? valueExtractor(value, input) : value; const filtered = suggestions.map(suggestion => String(suggestion)).filter(suggestion => fuzzytest(val, suggestion)).slice(0, 30).map(suggestion => ({ suggestion, innerHTML: fuzzywrap(val, suggestion) })); if (filtered.length === 1 && filtered[0].suggestion === val) { $$invalidate(4, filteredSuggestions = []); } else { $$invalidate(4, filteredSuggestions = filtered); } } } if ($$self.$$.dirty & /*index, filteredSuggestions*/ 80) { if (index > filteredSuggestions.length - 1) { $$invalidate(6, index = filteredSuggestions.length - 1); } } if ($$self.$$.dirty & /*setSize, value, placeholder*/ 16389) { if (setSize) { $$invalidate(8, inputOptions.size = Math.max(value.length, placeholder.length) + 1, inputOptions); } } }; return [ value, name, placeholder, className, filteredSuggestions, hidden, index, input, inputOptions, mousedown, keydown, suggestions, valueExtractor, valueSelector, setSize, focus, setCustomValidity, dispatch, select, input_1_input_handler, input_1_binding, blur_handler, focusin_handler, input_handler, mousedown_handler ]; } class AutocompleteInput extends SvelteComponent { constructor(options) { super(); if (!document.getElementById("svelte-16b8qvp-style")) add_css(); init(this, options, instance, create_fragment, safe_not_equal, { value: 0, suggestions: 11, name: 1, placeholder: 2, valueExtractor: 12, valueSelector: 13, setSize: 14, className: 3, focus: 15, setCustomValidity: 16 }); } get focus() { return this.$$.ctx[15]; } get setCustomValidity() { return this.$$.ctx[16]; } } /* javascript/entry-forms/AccountInput.svelte generated by Svelte v3.18.2 */ function create_fragment$1(ctx) { let updating_value; let current; function autocompleteinput_value_binding(value_1) { /*autocompleteinput_value_binding*/ ctx[5].call(null, value_1); } let autocompleteinput_props = { className: "account", placeholder: _("Account"), suggestions: /*suggestions*/ ctx[1] || favaAPI.accounts }; if (/*value*/ ctx[0] !== void 0) { autocompleteinput_props.value = /*value*/ ctx[0]; } const autocompleteinput = new AutocompleteInput({ props: autocompleteinput_props }); /*autocompleteinput_binding*/ ctx[4](autocompleteinput); binding_callbacks.push(() => bind(autocompleteinput, "value", autocompleteinput_value_binding)); return { c() { create_component(autocompleteinput.$$.fragment); }, m(target, anchor) { mount_component(autocompleteinput, target, anchor); current = true; }, p(ctx, [dirty]) { const autocompleteinput_changes = {}; if (dirty & /*suggestions*/ 2) autocompleteinput_changes.suggestions = /*suggestions*/ ctx[1] || favaAPI.accounts; if (!updating_value && dirty & /*value*/ 1) { updating_value = true; autocompleteinput_changes.value = /*value*/ ctx[0]; add_flush_callback(() => updating_value = false); } autocompleteinput.$set(autocompleteinput_changes); }, i(local) { if (current) return; transition_in(autocompleteinput.$$.fragment, local); current = true; }, o(local) { transition_out(autocompleteinput.$$.fragment, local); current = false; }, d(detaching) { /*autocompleteinput_binding*/ ctx[4](null); destroy_component(autocompleteinput, detaching); } }; } function instance$1($$self, $$props, $$invalidate) { let { value = "" } = $$props; let { suggestions } = $$props; let input; function checkValidity(val) { if (favaAPI.accounts.includes(val)) { input.setCustomValidity(""); } else { input.setCustomValidity(_("Should be one of the declared accounts")); } } function autocompleteinput_binding($$value) { binding_callbacks[$$value ? "unshift" : "push"](() => { $$invalidate(2, input = $$value); }); } function autocompleteinput_value_binding(value_1) { value = value_1; $$invalidate(0, value); } $$self.$set = $$props => { if ("value" in $$props) $$invalidate(0, value = $$props.value); if ("suggestions" in $$props) $$invalidate(1, suggestions = $$props.suggestions); }; $$self.$$.update = () => { if ($$self.$$.dirty & /*input, value*/ 5) { if (input) { checkValidity(value); } } }; return [ value, suggestions, input, checkValidity, autocompleteinput_binding, autocompleteinput_value_binding ]; } class AccountInput extends SvelteComponent { constructor(options) { super(); init(this, options, instance$1, create_fragment$1, safe_not_equal, { value: 0, suggestions: 1 }); } } /* javascript/Import.svelte generated by Svelte v3.18.2 */ function add_css$1() { var style = element("style"); style.id = "svelte-gepel5-style"; style.textContent = ".flex-row.svelte-gepel5{display:flex}.button.svelte-gepel5{padding:4px 8px}"; append(document.head, style); } function get_each_context_2(ctx, list, i) { const child_ctx = ctx.slice(); child_ctx[16] = list[i]; child_ctx[17] = list; child_ctx[18] = i; return child_ctx; } function get_each_context_1(ctx, list, i) { const child_ctx = ctx.slice(); child_ctx[13] = list[i]; child_ctx[14] = list; child_ctx[15] = i; return child_ctx; } function get_each_context$1(ctx, list, i) { const child_ctx = ctx.slice(); child_ctx[9] = list[i][0]; child_ctx[10] = list[i][1]; return child_ctx; } // (91:4) {:else} function create_else_block(ctx) { let p; let updating_value; let t0; let input; let t1; let button; let t3; let current; let dispose; function accountinput_value_binding_1(value) { /*accountinput_value_binding_1*/ ctx[6].call(null, value, /*item*/ ctx[13]); } let accountinput_props = {}; if (/*item*/ ctx[13].account !== void 0) { accountinput_props.value = /*item*/ ctx[13].account; } const accountinput = new AccountInput({ props: accountinput_props }); binding_callbacks.push(() => bind(accountinput, "value", accountinput_value_binding_1)); function input_input_handler_1() { /*input_input_handler_1*/ ctx[7].call(input, /*item*/ ctx[13]); } function click_handler_1(...args) { return /*click_handler_1*/ ctx[8](/*item*/ ctx[13], ...args); } return { c() { p = element("p"); create_component(accountinput.$$.fragment); t0 = space(); input = element("input"); t1 = space(); button = element("button"); button.textContent = `${"Move"}`; t3 = space(); attr(input, "size", "40"); attr(button, "type", "button"); }, m(target, anchor) { insert(target, p, anchor); mount_component(accountinput, p, null); append(p, t0); append(p, input); set_input_value(input, /*item*/ ctx[13].newName); append(p, t1); append(p, button); append(p, t3); current = true; dispose = [ listen(input, "input", input_input_handler_1), listen(button, "click", click_handler_1) ]; }, p(new_ctx, dirty) { ctx = new_ctx; const accountinput_changes = {}; if (!updating_value && dirty & /*Object, data*/ 1) { updating_value = true; accountinput_changes.value = /*item*/ ctx[13].account; add_flush_callback(() => updating_value = false); } accountinput.$set(accountinput_changes); if (dirty & /*Object, data*/ 1 && input.value !== /*item*/ ctx[13].newName) { set_input_value(input, /*item*/ ctx[13].newName); } }, i(local) { if (current) return; transition_in(accountinput.$$.fragment, local); current = true; }, o(local) { transition_out(accountinput.$$.fragment, local); current = false; }, d(detaching) { if (detaching) detach(p); destroy_component(accountinput); run_all(dispose); } }; } // (73:4) {#if item.importers.length} function create_if_block(ctx) { let each_1_anchor; let current; let each_value_2 = /*item*/ ctx[13].importers; let each_blocks = []; for (let i = 0; i < each_value_2.length; i += 1) { each_blocks[i] = create_each_block_2(get_each_context_2(ctx, each_value_2, i)); } const out = i => transition_out(each_blocks[i], 1, 1, () => { each_blocks[i] = null; }); return { c() { for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].c(); } each_1_anchor = empty(); }, m(target, anchor) { for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].m(target, anchor); } insert(target, each_1_anchor, anchor); current = true; }, p(ctx, dirty) { if (dirty & /*_, Object, data, extractURL, move*/ 3) { each_value_2 = /*item*/ ctx[13].importers; let i; for (i = 0; i < each_value_2.length; i += 1) { const child_ctx = get_each_context_2(ctx, each_value_2, i); if (each_blocks[i]) { each_blocks[i].p(child_ctx, dirty); transition_in(each_blocks[i], 1); } else { each_blocks[i] = create_each_block_2(child_ctx); each_blocks[i].c(); transition_in(each_blocks[i], 1); each_blocks[i].m(each_1_anchor.parentNode, each_1_anchor); } } group_outros(); for (i = each_value_2.length; i < each_blocks.length; i += 1) { out(i); } check_outros(); } }, i(local) { if (current) return; for (let i = 0; i < each_value_2.length; i += 1) { transition_in(each_blocks[i]); } current = true; }, o(local) { each_blocks = each_blocks.filter(Boolean); for (let i = 0; i < each_blocks.length; i += 1) { transition_out(each_blocks[i]); } current = false; }, d(detaching) { destroy_each(each_blocks, detaching); if (detaching) detach(each_1_anchor); } }; } // (74:6) {#each item.importers as info} function create_each_block_2(ctx) { let p; let updating_value; let t0; let input; let t1; let button; let t3; let a; let t4_value = _("Extract") + ""; let t4; let t5; let t6_value = /*info*/ ctx[16].importer_name + ""; let t6; let t7; let a_title_value; let a_href_value; let t8; let current; let dispose; function accountinput_value_binding(value) { /*accountinput_value_binding*/ ctx[3].call(null, value, /*info*/ ctx[16]); } let accountinput_props = {}; if (/*info*/ ctx[16].account !== void 0) { accountinput_props.value = /*info*/ ctx[16].account; } const accountinput = new AccountInput({ props: accountinput_props }); binding_callbacks.push(() => bind(accountinput, "value", accountinput_value_binding)); function input_input_handler() { /*input_input_handler*/ ctx[4].call(input, /*info*/ ctx[16]); } function click_handler(...args) { return /*click_handler*/ ctx[5](/*item*/ ctx[13], /*info*/ ctx[16], ...args); } return { c() { p = element("p"); create_component(accountinput.$$.fragment); t0 = space(); input = element("input"); t1 = space(); button = element("button"); button.textContent = `${"Move"}`; t3 = space(); a = element("a"); t4 = text(t4_value); t5 = text(" ( "); t6 = text(t6_value); t7 = text(" )"); t8 = space(); attr(input, "size", "40"); attr(button, "type", "button"); attr(a, "class", "button svelte-gepel5"); attr(a, "title", a_title_value = "" + (_("Extract") + " with importer " + /*info*/ ctx[16].importer_name)); attr(a, "href", a_href_value = extractURL(/*item*/ ctx[13].name, /*info*/ ctx[16].importer_name)); attr(p, "class", "flex-row svelte-gepel5"); }, m(target, anchor) { insert(target, p, anchor); mount_component(accountinput, p, null); append(p, t0); append(p, input); set_input_value(input, /*info*/ ctx[16].newName); append(p, t1); append(p, button); append(p, t3); append(p, a); append(a, t4); append(a, t5); append(a, t6); append(a, t7); append(p, t8); current = true; dispose = [ listen(input, "input", input_input_handler), listen(button, "click", click_handler) ]; }, p(new_ctx, dirty) { ctx = new_ctx; const accountinput_changes = {}; if (!updating_value && dirty & /*Object, data*/ 1) { updating_value = true; accountinput_changes.value = /*info*/ ctx[16].account; add_flush_callback(() => updating_value = false); } accountinput.$set(accountinput_changes); if (dirty & /*Object, data*/ 1 && input.value !== /*info*/ ctx[16].newName) { set_input_value(input, /*info*/ ctx[16].newName); } if ((!current || dirty & /*Object, data*/ 1) && t6_value !== (t6_value = /*info*/ ctx[16].importer_name + "")) set_data(t6, t6_value); if (!current || dirty & /*Object, data*/ 1 && a_title_value !== (a_title_value = "" + (_("Extract") + " with importer " + /*info*/ ctx[16].importer_name))) { attr(a, "title", a_title_value); } if (!current || dirty & /*Object, data*/ 1 && a_href_value !== (a_href_value = extractURL(/*item*/ ctx[13].name, /*info*/ ctx[16].importer_name))) { attr(a, "href", a_href_value); } }, i(local) { if (current) return; transition_in(accountinput.$$.fragment, local); current = true; }, o(local) { transition_out(accountinput.$$.fragment, local); current = false; }, d(detaching) { if (detaching) detach(p); destroy_component(accountinput); run_all(dispose); } }; } // (67:2) {#each items as item} function create_each_block_1(ctx) { let pre; let a; let t0_value = /*item*/ ctx[13].basename + ""; let t0; let a_href_value; let pre_title_value; let t1; let current_block_type_index; let if_block; let if_block_anchor; let current; const if_block_creators = [create_if_block, create_else_block]; const if_blocks = []; function select_block_type(ctx, dirty) { if (/*item*/ ctx[13].importers.length) return 0; return 1; } current_block_type_index = select_block_type(ctx); if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx); return { c() { pre = element("pre"); a = element("a"); t0 = text(t0_value); t1 = space(); if_block.c(); if_block_anchor = empty(); attr(a, "href", a_href_value = documentURL(/*item*/ ctx[13].name)); attr(a, "data-remote", ""); attr(a, "target", "_blank"); attr(pre, "title", pre_title_value = /*item*/ ctx[13].name); }, m(target, anchor) { insert(target, pre, anchor); append(pre, a); append(a, t0); insert(target, t1, anchor); if_blocks[current_block_type_index].m(target, anchor); insert(target, if_block_anchor, anchor); current = true; }, p(ctx, dirty) { if ((!current || dirty & /*Object, data*/ 1) && t0_value !== (t0_value = /*item*/ ctx[13].basename + "")) set_data(t0, t0_value); if (!current || dirty & /*Object, data*/ 1 && a_href_value !== (a_href_value = documentURL(/*item*/ ctx[13].name))) { attr(a, "href", a_href_value); } if (!current || dirty & /*Object, data*/ 1 && pre_title_value !== (pre_title_value = /*item*/ ctx[13].name)) { attr(pre, "title", pre_title_value); } let previous_block_index = current_block_type_index; current_block_type_index = select_block_type(ctx); if (current_block_type_index === previous_block_index) { if_blocks[current_block_type_index].p(ctx, dirty); } else { group_outros(); transition_out(if_blocks[previous_block_index], 1, 1, () => { if_blocks[previous_block_index] = null; }); check_outros(); if_block = if_blocks[current_block_type_index]; if (!if_block) { if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx); if_block.c(); } transition_in(if_block, 1); if_block.m(if_block_anchor.parentNode, if_block_anchor); } }, i(local) { if (current) return; transition_in(if_block); current = true; }, o(local) { transition_out(if_block); current = false; }, d(detaching) { if (detaching) detach(pre); if (detaching) detach(t1); if_blocks[current_block_type_index].d(detaching); if (detaching) detach(if_block_anchor); } }; } // (65:0) {#each Object.entries(data) as [directory, items]} function create_each_block$1(ctx) { let h3; let t0; let t1_value = /*directory*/ ctx[9] + ""; let t1; let t2; let each_1_anchor; let current; let each_value_1 = /*items*/ ctx[10]; let each_blocks = []; for (let i = 0; i < each_value_1.length; i += 1) { each_blocks[i] = create_each_block_1(get_each_context_1(ctx, each_value_1, i)); } const out = i => transition_out(each_blocks[i], 1, 1, () => { each_blocks[i] = null; }); return { c() { h3 = element("h3"); t0 = text("Directory: "); t1 = text(t1_value); t2 = space(); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].c(); } each_1_anchor = empty(); }, m(target, anchor) { insert(target, h3, anchor); append(h3, t0); append(h3, t1); insert(target, t2, anchor); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].m(target, anchor); } insert(target, each_1_anchor, anchor); current = true; }, p(ctx, dirty) { if ((!current || dirty & /*Object, data*/ 1) && t1_value !== (t1_value = /*directory*/ ctx[9] + "")) set_data(t1, t1_value); if (dirty & /*Object, data, _, extractURL, move, documentURL*/ 3) { each_value_1 = /*items*/ ctx[10]; let i; for (i = 0; i < each_value_1.length; i += 1) { const child_ctx = get_each_context_1(ctx, each_value_1, i); if (each_blocks[i]) { each_blocks[i].p(child_ctx, dirty); transition_in(each_blocks[i], 1); } else { each_blocks[i] = create_each_block_1(child_ctx); each_blocks[i].c(); transition_in(each_blocks[i], 1); each_blocks[i].m(each_1_anchor.parentNode, each_1_anchor); } } group_outros(); for (i = each_value_1.length; i < each_blocks.length; i += 1) { out(i); } check_outros(); } }, i(local) { if (current) return; for (let i = 0; i < each_value_1.length; i += 1) { transition_in(each_blocks[i]); } current = true; }, o(local) { each_blocks = each_blocks.filter(Boolean); for (let i = 0; i < each_blocks.length; i += 1) { transition_out(each_blocks[i]); } current = false; }, d(detaching) { if (detaching) detach(h3); if (detaching) detach(t2); destroy_each(each_blocks, detaching); if (detaching) detach(each_1_anchor); } }; } function create_fragment$2(ctx) { let each_1_anchor; let current; let each_value = Object.entries(/*data*/ ctx[0]); let each_blocks = []; for (let i = 0; i < each_value.length; i += 1) { each_blocks[i] = create_each_block$1(get_each_context$1(ctx, each_value, i)); } const out = i => transition_out(each_blocks[i], 1, 1, () => { each_blocks[i] = null; }); return { c() { for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].c(); } each_1_anchor = empty(); }, m(target, anchor) { for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].m(target, anchor); } insert(target, each_1_anchor, anchor); current = true; }, p(ctx, [dirty]) { if (dirty & /*Object, data, _, extractURL, move, documentURL*/ 3) { each_value = Object.entries(/*data*/ ctx[0]); let i; for (i = 0; i < each_value.length; i += 1) { const child_ctx = get_each_context$1(ctx, each_value, i); if (each_blocks[i]) { each_blocks[i].p(child_ctx, dirty); transition_in(each_blocks[i], 1); } else { each_blocks[i] = create_each_block$1(child_ctx); each_blocks[i].c(); transition_in(each_blocks[i], 1); each_blocks[i].m(each_1_anchor.parentNode, each_1_anchor); } } group_outros(); for (i = each_value.length; i < each_blocks.length; i += 1) { out(i); } check_outros(); } }, i(local) { if (current) return; for (let i = 0; i < each_value.length; i += 1) { transition_in(each_blocks[i]); } current = true; }, o(local) { each_blocks = each_blocks.filter(Boolean); for (let i = 0; i < each_blocks.length; i += 1) { transition_out(each_blocks[i]); } current = false; }, d(detaching) { destroy_each(each_blocks, detaching); if (detaching) detach(each_1_anchor); } }; } function newFilename(date, basename) { if ((/^\d{4}-\d{2}-\d{2}/).test(basename)) { return basename; } return `${date} ${basename}`; } function extractURL(filename, importer) { const params = new URLSearchParams(); params.set("filename", filename); params.set("importer", importer); return `#extract-${params.toString()}`; } function documentURL(filename) { const params = new URLSearchParams(); params.set("filename", filename); return `../document/?${params.toString()}`; } function instance$2($$self, $$props, $$invalidate) { let { data } = $$props; const today = todayAsString(); async function move(filename, account, newName) { const moved = await moveDocument(filename, account, newName); if (moved) { for (const [directory, items] of Object.entries(data)) { $$invalidate(0, data[directory] = items.filter(item => item.name !== filename), data); } } } function accountinput_value_binding(value, info) { info.account = value; } function input_input_handler(info) { info.newName = this.value; $$invalidate(0, data); } const click_handler = (item, info) => move(item.name, info.account, info.newName); function accountinput_value_binding_1(value, item) { item.account = value; } function input_input_handler_1(item) { item.newName = this.value; $$invalidate(0, data); } const click_handler_1 = item => move(item.name, item.account, item.newName); $$self.$set = $$props => { if ("data" in $$props) $$invalidate(0, data = $$props.data); }; $$self.$$.update = () => { if ($$self.$$.dirty & /*Object, data*/ 1) { // Initially set the file names for all importable files. for (const items of Object.values(data)) { for (const item of items) { item.newName = item.newName || newFilename(today, item.basename); for (const importInfo of item.importers) { importInfo.newName = importInfo.newName || newFilename(importInfo.date, importInfo.name); } } } } }; return [ data, move, today, accountinput_value_binding, input_input_handler, click_handler, accountinput_value_binding_1, input_input_handler_1, click_handler_1 ]; } class Import extends SvelteComponent { constructor(options) { super(); if (!document.getElementById("svelte-gepel5-style")) add_css$1(); init(this, options, instance$2, create_fragment$2, safe_not_equal, { data: 0 }); } } /* javascript/charts/ConversionAndInterval.svelte generated by Svelte v3.18.2 */ function get_each_context$2(ctx, list, i) { const child_ctx = ctx.slice(); child_ctx[6] = list[i]; return child_ctx; } function get_each_context_1$1(ctx, list, i) { const child_ctx = ctx.slice(); child_ctx[9] = list[i][0]; child_ctx[10] = list[i][1]; return child_ctx; } // (16:2) {#each $conversions as [conversion, conversionName]} function create_each_block_1$1(ctx) { let option; let t_value = /*conversionName*/ ctx[10] + ""; let t; let option_value_value; return { c() { option = element("option"); t = text(t_value); option.__value = option_value_value = /*conversion*/ ctx[9]; option.value = option.__value; }, m(target, anchor) { insert(target, option, anchor); append(option, t); }, p(ctx, dirty) { if (dirty & /*$conversions*/ 2 && t_value !== (t_value = /*conversionName*/ ctx[10] + "")) set_data(t, t_value); if (dirty & /*$conversions*/ 2 && option_value_value !== (option_value_value = /*conversion*/ ctx[9])) { option.__value = option_value_value; } option.value = option.__value; }, d(detaching) { if (detaching) detach(option); } }; } // (21:2) {#each Object.keys(intervals) as key} function create_each_block$2(ctx) { let option; let t_value = /*intervals*/ ctx[3][/*key*/ ctx[6]] + ""; let t; let option_value_value; return { c() { option = element("option"); t = text(t_value); option.__value = option_value_value = /*key*/ ctx[6]; option.value = option.__value; }, m(target, anchor) { insert(target, option, anchor); append(option, t); }, p: noop, d(detaching) { if (detaching) detach(option); } }; } function create_fragment$3(ctx) { let select0; let t; let select1; let dispose; let each_value_1 = /*$conversions*/ ctx[1]; let each_blocks_1 = []; for (let i = 0; i < each_value_1.length; i += 1) { each_blocks_1[i] = create_each_block_1$1(get_each_context_1$1(ctx, each_value_1, i)); } let each_value = Object.keys(/*intervals*/ ctx[3]); let each_blocks = []; for (let i = 0; i < each_value.length; i += 1) { each_blocks[i] = create_each_block$2(get_each_context$2(ctx, each_value, i)); } return { c() { select0 = element("select"); for (let i = 0; i < each_blocks_1.length; i += 1) { each_blocks_1[i].c(); } t = space(); select1 = element("select"); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].c(); } if (/*$conversion*/ ctx[0] === void 0) add_render_callback(() => /*select0_change_handler*/ ctx[4].call(select0)); if (/*$interval*/ ctx[2] === void 0) add_render_callback(() => /*select1_change_handler*/ ctx[5].call(select1)); }, m(target, anchor) { insert(target, select0, anchor); for (let i = 0; i < each_blocks_1.length; i += 1) { each_blocks_1[i].m(select0, null); } select_option(select0, /*$conversion*/ ctx[0]); insert(target, t, anchor); insert(target, select1, anchor); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].m(select1, null); } select_option(select1, /*$interval*/ ctx[2]); dispose = [ listen(select0, "change", /*select0_change_handler*/ ctx[4]), listen(select1, "change", /*select1_change_handler*/ ctx[5]) ]; }, p(ctx, [dirty]) { if (dirty & /*$conversions*/ 2) { each_value_1 = /*$conversions*/ ctx[1]; let i; for (i = 0; i < each_value_1.length; i += 1) { const child_ctx = get_each_context_1$1(ctx, each_value_1, i); if (each_blocks_1[i]) { each_blocks_1[i].p(child_ctx, dirty); } else { each_blocks_1[i] = create_each_block_1$1(child_ctx); each_blocks_1[i].c(); each_blocks_1[i].m(select0, null); } } for (; i < each_blocks_1.length; i += 1) { each_blocks_1[i].d(1); } each_blocks_1.length = each_value_1.length; } if (dirty & /*$conversion*/ 1) { select_option(select0, /*$conversion*/ ctx[0]); } if (dirty & /*Object, intervals*/ 8) { each_value = Object.keys(/*intervals*/ ctx[3]); let i; for (i = 0; i < each_value.length; i += 1) { const child_ctx = get_each_context$2(ctx, each_value, i); if (each_blocks[i]) { each_blocks[i].p(child_ctx, dirty); } else { each_blocks[i] = create_each_block$2(child_ctx); each_blocks[i].c(); each_blocks[i].m(select1, null); } } for (; i < each_blocks.length; i += 1) { each_blocks[i].d(1); } each_blocks.length = each_value.length; } if (dirty & /*$interval*/ 4) { select_option(select1, /*$interval*/ ctx[2]); } }, i: noop, o: noop, d(detaching) { if (detaching) detach(select0); destroy_each(each_blocks_1, detaching); if (detaching) detach(t); if (detaching) detach(select1); destroy_each(each_blocks, detaching); run_all(dispose); } }; } function instance$3($$self, $$props, $$invalidate) { let $conversion; let $conversions; let $interval; component_subscribe($$self, conversion, $$value => $$invalidate(0, $conversion = $$value)); component_subscribe($$self, conversions, $$value => $$invalidate(1, $conversions = $$value)); component_subscribe($$self, interval, $$value => $$invalidate(2, $interval = $$value)); const intervals = { year: _("Yearly"), quarter: _("Quarterly"), month: _("Monthly"), week: _("Weekly"), day: _("Daily") }; function select0_change_handler() { $conversion = select_value(this); conversion.set($conversion); } function select1_change_handler() { $interval = select_value(this); interval.set($interval); $$invalidate(3, intervals); } return [ $conversion, $conversions, $interval, intervals, select0_change_handler, select1_change_handler ]; } class ConversionAndInterval extends SvelteComponent { constructor(options) { super(); init(this, options, instance$3, create_fragment$3, safe_not_equal, {}); } } /* javascript/charts/Chart.svelte generated by Svelte v3.18.2 */ function get_each_context$3(ctx, list, i) { const child_ctx = ctx.slice(); child_ctx[21] = list[i]; return child_ctx; } function get_each_context_1$2(ctx, list, i) { const child_ctx = ctx.slice(); child_ctx[24] = list[i]; return child_ctx; } // (47:4) {#each legend.domain.sort() as item} function create_each_block_1$2(ctx) { let span; let i; let t0; let t1_value = /*item*/ ctx[24] + ""; let t1; let t2; return { c() { span = element("span"); i = element("i"); t0 = space(); t1 = text(t1_value); t2 = space(); attr(i, "class", "color"); set_style(i, "background-color", /*legend*/ ctx[5].scale(/*item*/ ctx[24])); attr(span, "class", "legend"); }, m(target, anchor) { insert(target, span, anchor); append(span, i); append(span, t0); append(span, t1); append(span, t2); }, p(ctx, dirty) { if (dirty & /*legend*/ 32) { set_style(i, "background-color", /*legend*/ ctx[5].scale(/*item*/ ctx[24])); } if (dirty & /*legend*/ 32 && t1_value !== (t1_value = /*item*/ ctx[24] + "")) set_data(t1, t1_value); }, d(detaching) { if (detaching) detach(span); } }; } // (58:4) {#each currencies as currency} function create_each_block$3(ctx) { let option; let t_value = /*currency*/ ctx[21] + ""; let t; let option_value_value; return { c() { option = element("option"); t = text(t_value); option.__value = option_value_value = /*currency*/ ctx[21]; option.value = option.__value; }, m(target, anchor) { insert(target, option, anchor); append(option, t); }, p(ctx, dirty) { if (dirty & /*currencies*/ 64 && t_value !== (t_value = /*currency*/ ctx[21] + "")) set_data(t, t_value); if (dirty & /*currencies*/ 64 && option_value_value !== (option_value_value = /*currency*/ ctx[21])) { option.__value = option_value_value; } option.value = option.__value; }, d(detaching) { if (detaching) detach(option); } }; } function create_fragment$4(ctx) { let form; let p; let p_hidden_value; let t0; let span0; let t1; let select; let select_hidden_value; let t2; let span3; let label0; let input0; let t3; let span1; let t5; let label1; let input1; let t6; let span2; let span3_hidden_value; let t8; let t9; let button; let t10; let div; let svg_1; let div_hidden_value; let div_resize_listener; let current; let dispose; let each_value_1 = /*legend*/ ctx[5].domain.sort(); let each_blocks_1 = []; for (let i = 0; i < each_value_1.length; i += 1) { each_blocks_1[i] = create_each_block_1$2(get_each_context_1$2(ctx, each_value_1, i)); } let each_value = /*currencies*/ ctx[6]; let each_blocks = []; for (let i = 0; i < each_value.length; i += 1) { each_blocks[i] = create_each_block$3(get_each_context$3(ctx, each_value, i)); } const default_slot_template = /*$$slots*/ ctx[13].default; const default_slot = create_slot(default_slot_template, ctx, /*$$scope*/ ctx[12], null); return { c() { form = element("form"); p = element("p"); for (let i = 0; i < each_blocks_1.length; i += 1) { each_blocks_1[i].c(); } t0 = space(); span0 = element("span"); t1 = space(); select = element("select"); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].c(); } t2 = space(); span3 = element("span"); label0 = element("label"); input0 = element("input"); t3 = space(); span1 = element("span"); span1.textContent = `${_("Treemap")}`; t5 = space(); label1 = element("label"); input1 = element("input"); t6 = space(); span2 = element("span"); span2.textContent = `${_("Sunburst")}`; t8 = space(); if (default_slot) default_slot.c(); t9 = space(); button = element("button"); t10 = space(); div = element("div"); svg_1 = svg_element("svg"); p.hidden = p_hidden_value = !/*$showCharts*/ ctx[8]; attr(p, "class", "chart-legend"); attr(span0, "class", "spacer"); select.hidden = select_hidden_value = !/*$showCharts*/ ctx[8] || !/*hasCurrencySetting*/ ctx[1]; if (/*$chartCurrency*/ ctx[4] === void 0) add_render_callback(() => /*select_change_handler*/ ctx[14].call(select)); attr(input0, "type", "radio"); input0.__value = "treemap"; input0.value = input0.__value; /*$$binding_groups*/ ctx[16][0].push(input0); attr(span1, "class", "button"); attr(input1, "type", "radio"); input1.__value = "sunburst"; input1.value = input1.__value; /*$$binding_groups*/ ctx[16][0].push(input1); attr(span2, "class", "button"); span3.hidden = span3_hidden_value = !/*$showCharts*/ ctx[8] || !/*hasModeSetting*/ ctx[7]; attr(span3, "class", "chart-mode"); attr(button, "type", "button"); attr(button, "data-key", "ctrl+c"); attr(button, "class", "toggle-chart"); toggle_class(button, "closed", !/*$showCharts*/ ctx[8]); attr(form, "class", "wide-form"); div.hidden = div_hidden_value = !/*$showCharts*/ ctx[8]; add_render_callback(() => /*div_elementresize_handler*/ ctx[20].call(div)); }, m(target, anchor) { insert(target, form, anchor); append(form, p); for (let i = 0; i < each_blocks_1.length; i += 1) { each_blocks_1[i].m(p, null); } append(form, t0); append(form, span0); append(form, t1); append(form, select); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].m(select, null); } select_option(select, /*$chartCurrency*/ ctx[4]); append(form, t2); append(form, span3); append(span3, label0); append(label0, input0); input0.checked = input0.__value === /*$chartMode*/ ctx[3]; append(label0, t3); append(label0, span1); append(span3, t5); append(span3, label1); append(label1, input1); input1.checked = input1.__value === /*$chartMode*/ ctx[3]; append(label1, t6); append(label1, span2); append(form, t8); if (default_slot) { default_slot.m(form, null); } append(form, t9); append(form, button); insert(target, t10, anchor); insert(target, div, anchor); append(div, svg_1); /*svg_1_binding*/ ctx[19](svg_1); div_resize_listener = add_resize_listener(div, /*div_elementresize_handler*/ ctx[20].bind(div)); current = true; dispose = [ listen(select, "change", /*select_change_handler*/ ctx[14]), listen(input0, "change", /*input0_change_handler*/ ctx[15]), listen(input1, "change", /*input1_change_handler*/ ctx[17]), listen(button, "click", /*click_handler*/ ctx[18]) ]; }, p(ctx, [dirty]) { if (dirty & /*legend*/ 32) { each_value_1 = /*legend*/ ctx[5].domain.sort(); let i; for (i = 0; i < each_value_1.length; i += 1) { const child_ctx = get_each_context_1$2(ctx, each_value_1, i); if (each_blocks_1[i]) { each_blocks_1[i].p(child_ctx, dirty); } else { each_blocks_1[i] = create_each_block_1$2(child_ctx); each_blocks_1[i].c(); each_blocks_1[i].m(p, null); } } for (; i < each_blocks_1.length; i += 1) { each_blocks_1[i].d(1); } each_blocks_1.length = each_value_1.length; } if (!current || dirty & /*$showCharts*/ 256 && p_hidden_value !== (p_hidden_value = !/*$showCharts*/ ctx[8])) { p.hidden = p_hidden_value; } if (dirty & /*currencies*/ 64) { each_value = /*currencies*/ ctx[6]; let i; for (i = 0; i < each_value.length; i += 1) { const child_ctx = get_each_context$3(ctx, each_value, i); if (each_blocks[i]) { each_blocks[i].p(child_ctx, dirty); } else { each_blocks[i] = create_each_block$3(child_ctx); each_blocks[i].c(); each_blocks[i].m(select, null); } } for (; i < each_blocks.length; i += 1) { each_blocks[i].d(1); } each_blocks.length = each_value.length; } if (!current || dirty & /*$showCharts, hasCurrencySetting*/ 258 && select_hidden_value !== (select_hidden_value = !/*$showCharts*/ ctx[8] || !/*hasCurrencySetting*/ ctx[1])) { select.hidden = select_hidden_value; } if (dirty & /*$chartCurrency*/ 16) { select_option(select, /*$chartCurrency*/ ctx[4]); } if (dirty & /*$chartMode*/ 8) { input0.checked = input0.__value === /*$chartMode*/ ctx[3]; } if (dirty & /*$chartMode*/ 8) { input1.checked = input1.__value === /*$chartMode*/ ctx[3]; } if (!current || dirty & /*$showCharts, hasModeSetting*/ 384 && span3_hidden_value !== (span3_hidden_value = !/*$showCharts*/ ctx[8] || !/*hasModeSetting*/ ctx[7])) { span3.hidden = span3_hidden_value; } if (default_slot && default_slot.p && dirty & /*$$scope*/ 4096) { default_slot.p(get_slot_context(default_slot_template, ctx, /*$$scope*/ ctx[12], null), get_slot_changes(default_slot_template, /*$$scope*/ ctx[12], dirty, null)); } if (dirty & /*$showCharts*/ 256) { toggle_class(button, "closed", !/*$showCharts*/ ctx[8]); } if (!current || dirty & /*$showCharts*/ 256 && div_hidden_value !== (div_hidden_value = !/*$showCharts*/ ctx[8])) { div.hidden = div_hidden_value; } }, i(local) { if (current) return; transition_in(default_slot, local); current = true; }, o(local) { transition_out(default_slot, local); current = false; }, d(detaching) { if (detaching) detach(form); destroy_each(each_blocks_1, detaching); destroy_each(each_blocks, detaching); /*$$binding_groups*/ ctx[16][0].splice(/*$$binding_groups*/ ctx[16][0].indexOf(input0), 1); /*$$binding_groups*/ ctx[16][0].splice(/*$$binding_groups*/ ctx[16][0].indexOf(input1), 1); if (default_slot) default_slot.d(detaching); if (detaching) detach(t10); if (detaching) detach(div); /*svg_1_binding*/ ctx[19](null); div_resize_listener.cancel(); run_all(dispose); } }; } function instance$4($$self, $$props, $$invalidate) { let $chartMode; let $chartCurrency; let $showCharts; component_subscribe($$self, chartMode, $$value => $$invalidate(3, $chartMode = $$value)); component_subscribe($$self, chartCurrency, $$value => $$invalidate(4, $chartCurrency = $$value)); component_subscribe($$self, showCharts, $$value => $$invalidate(8, $showCharts = $$value)); let { chart } = $$props; let svg; let renderedChart; let hasCurrencySetting; let chartWidth; async function chartChanged() { await tick(); if (!svg) { return; } $$invalidate(10, renderedChart = chart.renderer(svg).setWidth(chartWidth).set("mode", $chartMode).set("currency", $chartCurrency).draw(chart.data)); $$invalidate(1, hasCurrencySetting = renderedChart.has_currency_setting); } let { $$slots = {}, $$scope } = $$props; const $$binding_groups = [[]]; function select_change_handler() { $chartCurrency = select_value(this); chartCurrency.set($chartCurrency); ($$invalidate(6, currencies), $$invalidate(10, renderedChart)); } function input0_change_handler() { $chartMode = this.__value; chartMode.set($chartMode); } function input1_change_handler() { $chartMode = this.__value; chartMode.set($chartMode); } const click_handler = () => { showCharts.update(v => !v); }; function svg_1_binding($$value) { binding_callbacks[$$value ? "unshift" : "push"](() => { $$invalidate(0, svg = $$value); }); } function div_elementresize_handler() { chartWidth = this.clientWidth; $$invalidate(2, chartWidth); } $$self.$set = $$props => { if ("chart" in $$props) $$invalidate(9, chart = $$props.chart); if ("$$scope" in $$props) $$invalidate(12, $$scope = $$props.$$scope); }; let legend; let currencies; let hasModeSetting; $$self.$$.update = () => { if ($$self.$$.dirty & /*chart*/ 512) { if (chart) { chartChanged(); } } if ($$self.$$.dirty & /*renderedChart, chartWidth, $chartMode, $chartCurrency*/ 1052) { if (renderedChart) { renderedChart.setWidth(chartWidth).set("mode", $chartMode).set("currency", $chartCurrency).update(); $$invalidate(1, hasCurrencySetting = renderedChart.has_currency_setting); } } if ($$self.$$.dirty & /*renderedChart*/ 1024) { $$invalidate(5, legend = renderedChart && renderedChart.legend || { domain: [] }); } if ($$self.$$.dirty & /*renderedChart*/ 1024) { $$invalidate(6, currencies = renderedChart && renderedChart.currencies || []); } if ($$self.$$.dirty & /*renderedChart*/ 1024) { $$invalidate(7, hasModeSetting = renderedChart && renderedChart.has_mode_setting); } }; return [ svg, hasCurrencySetting, chartWidth, $chartMode, $chartCurrency, legend, currencies, hasModeSetting, $showCharts, chart, renderedChart, chartChanged, $$scope, $$slots, select_change_handler, input0_change_handler, $$binding_groups, input1_change_handler, click_handler, svg_1_binding, div_elementresize_handler ]; } class Chart extends SvelteComponent { constructor(options) { super(); init(this, options, instance$4, create_fragment$4, safe_not_equal, { chart: 9 }); } } /* javascript/charts/ChartSwitcher.svelte generated by Svelte v3.18.2 */ function get_each_context$4(ctx, list, i) { const child_ctx = ctx.slice(); child_ctx[4] = list[i]; return child_ctx; } // (21:0) {#if charts.length} function create_if_block$1(ctx) { let t; let div; let div_hidden_value; let current; const chart = new Chart({ props: { chart: /*$activeChart*/ ctx[1], $$slots: { default: [create_default_slot] }, $$scope: { ctx } } }); let each_value = /*charts*/ ctx[0]; let each_blocks = []; for (let i = 0; i < each_value.length; i += 1) { each_blocks[i] = create_each_block$4(get_each_context$4(ctx, each_value, i)); } return { c() { create_component(chart.$$.fragment); t = space(); div = element("div"); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].c(); } div.hidden = div_hidden_value = !/*$showCharts*/ ctx[2]; attr(div, "class", "chart-labels"); }, m(target, anchor) { mount_component(chart, target, anchor); insert(target, t, anchor); insert(target, div, anchor); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].m(div, null); } current = true; }, p(ctx, dirty) { const chart_changes = {}; if (dirty & /*$activeChart*/ 2) chart_changes.chart = /*$activeChart*/ ctx[1]; if (dirty & /*$$scope*/ 128) { chart_changes.$$scope = { dirty, ctx }; } chart.$set(chart_changes); if (dirty & /*charts, $activeChart*/ 3) { each_value = /*charts*/ ctx[0]; let i; for (i = 0; i < each_value.length; i += 1) { const child_ctx = get_each_context$4(ctx, each_value, i); if (each_blocks[i]) { each_blocks[i].p(child_ctx, dirty); } else { each_blocks[i] = create_each_block$4(child_ctx); each_blocks[i].c(); each_blocks[i].m(div, null); } } for (; i < each_blocks.length; i += 1) { each_blocks[i].d(1); } each_blocks.length = each_value.length; } if (!current || dirty & /*$showCharts*/ 4 && div_hidden_value !== (div_hidden_value = !/*$showCharts*/ ctx[2])) { div.hidden = div_hidden_value; } }, i(local) { if (current) return; transition_in(chart.$$.fragment, local); current = true; }, o(local) { transition_out(chart.$$.fragment, local); current = false; }, d(detaching) { destroy_component(chart, detaching); if (detaching) detach(t); if (detaching) detach(div); destroy_each(each_blocks, detaching); } }; } // (22:2) function create_default_slot(ctx) { let current; const conversionandinterval = new ConversionAndInterval({}); return { c() { create_component(conversionandinterval.$$.fragment); }, m(target, anchor) { mount_component(conversionandinterval, target, anchor); current = true; }, i(local) { if (current) return; transition_in(conversionandinterval.$$.fragment, local); current = true; }, o(local) { transition_out(conversionandinterval.$$.fragment, local); current = false; }, d(detaching) { destroy_component(conversionandinterval, detaching); } }; } // (26:4) {#each charts as chart} function create_each_block$4(ctx) { let label; let t0_value = /*chart*/ ctx[4].name + ""; let t0; let t1; let dispose; function click_handler(...args) { return /*click_handler*/ ctx[3](/*chart*/ ctx[4], ...args); } return { c() { label = element("label"); t0 = text(t0_value); t1 = space(); toggle_class(label, "selected", /*chart*/ ctx[4] === /*$activeChart*/ ctx[1]); }, m(target, anchor) { insert(target, label, anchor); append(label, t0); append(label, t1); dispose = listen(label, "click", click_handler); }, p(new_ctx, dirty) { ctx = new_ctx; if (dirty & /*charts*/ 1 && t0_value !== (t0_value = /*chart*/ ctx[4].name + "")) set_data(t0, t0_value); if (dirty & /*charts, $activeChart*/ 3) { toggle_class(label, "selected", /*chart*/ ctx[4] === /*$activeChart*/ ctx[1]); } }, d(detaching) { if (detaching) detach(label); dispose(); } }; } function create_fragment$5(ctx) { let if_block_anchor; let current; let if_block = /*charts*/ ctx[0].length && create_if_block$1(ctx); return { c() { if (if_block) if_block.c(); if_block_anchor = empty(); }, m(target, anchor) { if (if_block) if_block.m(target, anchor); insert(target, if_block_anchor, anchor); current = true; }, p(ctx, [dirty]) { if (/*charts*/ ctx[0].length) { if (if_block) { if_block.p(ctx, dirty); transition_in(if_block, 1); } else { if_block = create_if_block$1(ctx); if_block.c(); transition_in(if_block, 1); if_block.m(if_block_anchor.parentNode, if_block_anchor); } } else if (if_block) { group_outros(); transition_out(if_block, 1, 1, () => { if_block = null; }); check_outros(); } }, i(local) { if (current) return; transition_in(if_block); current = true; }, o(local) { transition_out(if_block); current = false; }, d(detaching) { if (if_block) if_block.d(detaching); if (detaching) detach(if_block_anchor); } }; } function instance$5($$self, $$props, $$invalidate) { let $activeChart; let $showCharts; component_subscribe($$self, activeChart, $$value => $$invalidate(1, $activeChart = $$value)); component_subscribe($$self, showCharts, $$value => $$invalidate(2, $showCharts = $$value)); let charts = []; onMount(() => { $$invalidate(0, charts = parseChartData()); if (charts.length) { set_store_value(activeChart, $activeChart = charts.find(c => c.name === $activeChart.name) || charts[0]); } }); const click_handler = chart => { set_store_value(activeChart, $activeChart = chart); }; return [charts, $activeChart, $showCharts, click_handler]; } class ChartSwitcher extends SvelteComponent { constructor(options) { super(); init(this, options, instance$5, create_fragment$5, safe_not_equal, {}); } } /* javascript/FilterForm.svelte generated by Svelte v3.18.2 */ function get_each_context$5(ctx, list, i) { const child_ctx = ctx.slice(); child_ctx[5] = list[i].name; child_ctx[6] = list[i].placeholder; child_ctx[7] = list[i].key; child_ctx[8] = list[i].suggestions; child_ctx[9] = list[i].autocompleteOptions; return child_ctx; } // (68:2) {#each filters as { name, placeholder, key, suggestions, autocompleteOptions }} function create_each_block$5(ctx) { let span; let updating_value; let t0; let button; let current; let dispose; const autocompleteinput_spread_levels = [ { placeholder: /*placeholder*/ ctx[6] }, { suggestions: /*suggestions*/ ctx[8] }, { setSize: true }, /*autocompleteOptions*/ ctx[9] ]; function autocompleteinput_value_binding(value) { /*autocompleteinput_value_binding*/ ctx[3].call(null, value, /*name*/ ctx[5]); } let autocompleteinput_props = {}; for (let i = 0; i < autocompleteinput_spread_levels.length; i += 1) { autocompleteinput_props = assign(autocompleteinput_props, autocompleteinput_spread_levels[i]); } if (/*values*/ ctx[0][/*name*/ ctx[5]] !== void 0) { autocompleteinput_props.value = /*values*/ ctx[0][/*name*/ ctx[5]]; } const autocompleteinput = new AutocompleteInput({ props: autocompleteinput_props }); binding_callbacks.push(() => bind(autocompleteinput, "value", autocompleteinput_value_binding)); autocompleteinput.$on("select", /*submit*/ ctx[2]); function click_handler(...args) { return /*click_handler*/ ctx[4](/*name*/ ctx[5], ...args); } return { c() { span = element("span"); create_component(autocompleteinput.$$.fragment); t0 = space(); button = element("button"); button.textContent = "×"; attr(button, "type", "button"); attr(button, "tabindex", "-1"); attr(button, "class", "close muted round"); toggle_class(span, "empty", !/*values*/ ctx[0][/*name*/ ctx[5]]); }, m(target, anchor) { insert(target, span, anchor); mount_component(autocompleteinput, span, null); append(span, t0); append(span, button); current = true; dispose = listen(button, "click", click_handler); }, p(new_ctx, dirty) { ctx = new_ctx; const autocompleteinput_changes = (dirty & /*filters*/ 2) ? get_spread_update(autocompleteinput_spread_levels, [ autocompleteinput_spread_levels[0], autocompleteinput_spread_levels[1], autocompleteinput_spread_levels[2], get_spread_object(/*autocompleteOptions*/ ctx[9]) ]) : {}; if (!updating_value && dirty & /*values, filters*/ 3) { updating_value = true; autocompleteinput_changes.value = /*values*/ ctx[0][/*name*/ ctx[5]]; add_flush_callback(() => updating_value = false); } autocompleteinput.$set(autocompleteinput_changes); if (dirty & /*values, filters*/ 3) { toggle_class(span, "empty", !/*values*/ ctx[0][/*name*/ ctx[5]]); } }, i(local) { if (current) return; transition_in(autocompleteinput.$$.fragment, local); current = true; }, o(local) { transition_out(autocompleteinput.$$.fragment, local); current = false; }, d(detaching) { if (detaching) detach(span); destroy_component(autocompleteinput); dispose(); } }; } function create_fragment$6(ctx) { let form; let t; let button; let current; let dispose; let each_value = /*filters*/ ctx[1]; let each_blocks = []; for (let i = 0; i < each_value.length; i += 1) { each_blocks[i] = create_each_block$5(get_each_context$5(ctx, each_value, i)); } const out = i => transition_out(each_blocks[i], 1, 1, () => { each_blocks[i] = null; }); return { c() { form = element("form"); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].c(); } t = space(); button = element("button"); attr(button, "type", "submit"); attr(form, "id", "filter-form"); attr(form, "class", "filter-form"); }, m(target, anchor) { insert(target, form, anchor); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].m(form, null); } append(form, t); append(form, button); current = true; dispose = listen(form, "submit", prevent_default(/*submit*/ ctx[2])); }, p(ctx, [dirty]) { if (dirty & /*values, filters, clear, submit*/ 7) { each_value = /*filters*/ ctx[1]; let i; for (i = 0; i < each_value.length; i += 1) { const child_ctx = get_each_context$5(ctx, each_value, i); if (each_blocks[i]) { each_blocks[i].p(child_ctx, dirty); transition_in(each_blocks[i], 1); } else { each_blocks[i] = create_each_block$5(child_ctx); each_blocks[i].c(); transition_in(each_blocks[i], 1); each_blocks[i].m(form, t); } } group_outros(); for (i = each_value.length; i < each_blocks.length; i += 1) { out(i); } check_outros(); } }, i(local) { if (current) return; for (let i = 0; i < each_value.length; i += 1) { transition_in(each_blocks[i]); } current = true; }, o(local) { each_blocks = each_blocks.filter(Boolean); for (let i = 0; i < each_blocks.length; i += 1) { transition_out(each_blocks[i]); } current = false; }, d(detaching) { if (detaching) detach(form); destroy_each(each_blocks, detaching); dispose(); } }; } function clear(name) { filters.update(fs => { const ret = { ...fs }; ret[name] = ""; return ret; }); } function instance$6($$self, $$props, $$invalidate) { const filters$1 = [ { name: "time", placeholder: _("Time"), key: "f t", suggestions: favaAPI.years }, { name: "account", placeholder: _("Account"), key: "f a", suggestions: favaAPI.accounts }, { name: "filter", placeholder: _("Filter by tag, payee, ..."), key: "f f", suggestions: [ ...favaAPI.tags.map(tag => `#${tag}`), ...favaAPI.links.map(link => `^${link}`), ...favaAPI.payees.map(payee => `payee:"${payee}"`) ], autocompleteOptions: { valueExtractor(value, input) { const [ret] = value.slice(0, input.selectionStart).match(/\S*$/); return ret; }, valueSelector(value, input) { const [search] = input.value.slice(0, input.selectionStart).match(/\S*$/); return `${input.value.slice(0, input.selectionStart - search.length)}${value}${input.value.slice(input.selectionStart)}`; } } } ]; let values; filters.subscribe(fs => { $$invalidate(0, values = { ...fs }); }); function submit() { filters.update(fs => { Object.assign(fs, values); return fs; }); } function autocompleteinput_value_binding(value, name) { values[name] = value; $$invalidate(0, values); } const click_handler = name => clear(name); return [values, filters$1, submit, autocompleteinput_value_binding, click_handler]; } class FilterForm extends SvelteComponent { constructor(options) { super(); init(this, options, instance$6, create_fragment$6, safe_not_equal, {}); } } function basename(filename) { const parts = filename.split("/"); return parts[parts.length - 1]; } /** * Generate an account tree from an array of entries. */ function entriesToTree(data) { const groups = group(data, e => e.account); const root = { name: "", fullname: "", children: new Map() }; for (const account of [...groups.keys()].sort()) { let node = root; let parent; const parts = account.split(":"); for (const part of parts) { parent = node; node = parent.children.get(part); if (!node) { node = { name: part, fullname: parent.fullname ? `${parent.fullname}:${part}` : part, children: new Map(), }; parent.children.set(part, node); } } } return root; } /* javascript/modals/ModalBase.svelte generated by Svelte v3.18.2 */ function create_fragment$7(ctx) { let div2; let div0; let t0; let div1; let button; let t2; let current; let dispose; const default_slot_template = /*$$slots*/ ctx[3].default; const default_slot = create_slot(default_slot_template, ctx, /*$$scope*/ ctx[2], null); return { c() { div2 = element("div"); div0 = element("div"); t0 = space(); div1 = element("div"); button = element("button"); button.textContent = "x"; t2 = space(); if (default_slot) default_slot.c(); attr(div0, "class", "overlay-background"); attr(button, "type", "button"); attr(button, "class", "muted close-overlay"); attr(div1, "class", "overlay-content"); attr(div2, "class", "overlay"); toggle_class(div2, "shown", /*shown*/ ctx[0]); }, m(target, anchor) { insert(target, div2, anchor); append(div2, div0); append(div2, t0); append(div2, div1); append(div1, button); append(div1, t2); if (default_slot) { default_slot.m(div1, null); } current = true; dispose = [ listen(div0, "click", function () { if (is_function(/*closeHandler*/ ctx[1])) /*closeHandler*/ ctx[1].apply(this, arguments); }), listen(button, "click", function () { if (is_function(/*closeHandler*/ ctx[1])) /*closeHandler*/ ctx[1].apply(this, arguments); }) ]; }, p(new_ctx, [dirty]) { ctx = new_ctx; if (default_slot && default_slot.p && dirty & /*$$scope*/ 4) { default_slot.p(get_slot_context(default_slot_template, ctx, /*$$scope*/ ctx[2], null), get_slot_changes(default_slot_template, /*$$scope*/ ctx[2], dirty, null)); } if (dirty & /*shown*/ 1) { toggle_class(div2, "shown", /*shown*/ ctx[0]); } }, i(local) { if (current) return; transition_in(default_slot, local); current = true; }, o(local) { transition_out(default_slot, local); current = false; }, d(detaching) { if (detaching) detach(div2); if (default_slot) default_slot.d(detaching); run_all(dispose); } }; } function instance$7($$self, $$props, $$invalidate) { let { shown = false } = $$props; let { closeHandler = closeOverlay } = $$props; let { $$slots = {}, $$scope } = $$props; $$self.$set = $$props => { if ("shown" in $$props) $$invalidate(0, shown = $$props.shown); if ("closeHandler" in $$props) $$invalidate(1, closeHandler = $$props.closeHandler); if ("$$scope" in $$props) $$invalidate(2, $$scope = $$props.$$scope); }; return [shown, closeHandler, $$scope, $$slots]; } class ModalBase extends SvelteComponent { constructor(options) { super(); init(this, options, instance$7, create_fragment$7, safe_not_equal, { shown: 0, closeHandler: 1 }); } } const selectedAccount = writable(""); /* javascript/documents/Accounts.svelte generated by Svelte v3.18.2 */ function add_css$2() { var style = element("style"); style.id = "svelte-g7jz3i-style"; style.textContent = "ul.svelte-g7jz3i{padding:0 0 0 0.5em}p.svelte-g7jz3i{border:1px solid var(--color-table-border);margin-bottom:-1px;overflow:hidden;cursor:pointer}.selected.svelte-g7jz3i,.drag.svelte-g7jz3i{background-color:var(--color-table-header-background)}"; append(document.head, style); } function get_each_context$6(ctx, list, i) { const child_ctx = ctx.slice(); child_ctx[10] = list[i]; return child_ctx; } // (50:0) {#if node.name} function create_if_block_2(ctx) { let p; let span; let t_value = /*node*/ ctx[0].name + ""; let t; let p_title_value; let p_data_account_name_value; let dispose; return { c() { p = element("p"); span = element("span"); t = text(t_value); attr(p, "title", p_title_value = /*node*/ ctx[0].fullname); attr(p, "class", "droptarget svelte-g7jz3i"); attr(p, "data-account-name", p_data_account_name_value = /*node*/ ctx[0].fullname); toggle_class(p, "expanded", expanded); toggle_class(p, "selected", /*$selectedAccount*/ ctx[2] === /*node*/ ctx[0].fullname); toggle_class(p, "drag", /*drag*/ ctx[1]); }, m(target, anchor) { insert(target, p, anchor); append(p, span); append(span, t); dispose = [ listen(p, "click", /*click*/ ctx[3]), listen(p, "dragenter", /*dragenter*/ ctx[4]), listen(p, "dragover", /*dragover*/ ctx[5]), listen(p, "dragleave", /*dragleave_handler*/ ctx[8]), listen(p, "drop", prevent_default(/*drop*/ ctx[6])) ]; }, p(ctx, dirty) { if (dirty & /*node*/ 1 && t_value !== (t_value = /*node*/ ctx[0].name + "")) set_data(t, t_value); if (dirty & /*node*/ 1 && p_title_value !== (p_title_value = /*node*/ ctx[0].fullname)) { attr(p, "title", p_title_value); } if (dirty & /*node*/ 1 && p_data_account_name_value !== (p_data_account_name_value = /*node*/ ctx[0].fullname)) { attr(p, "data-account-name", p_data_account_name_value); } if (dirty & /*expanded*/ 0) { toggle_class(p, "expanded", expanded); } if (dirty & /*$selectedAccount, node*/ 5) { toggle_class(p, "selected", /*$selectedAccount*/ ctx[2] === /*node*/ ctx[0].fullname); } if (dirty & /*drag*/ 2) { toggle_class(p, "drag", /*drag*/ ctx[1]); } }, d(detaching) { if (detaching) detach(p); run_all(dispose); } }; } // (69:0) {#if node.children.size} function create_if_block$2(ctx) { let ul; let ul_hidden_value; let current; let each_value = [.../*node*/ ctx[0].children.values()]; let each_blocks = []; for (let i = 0; i < each_value.length; i += 1) { each_blocks[i] = create_each_block$6(get_each_context$6(ctx, each_value, i)); } const out = i => transition_out(each_blocks[i], 1, 1, () => { each_blocks[i] = null; }); return { c() { ul = element("ul"); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].c(); } attr(ul, "class", "flex-table svelte-g7jz3i"); ul.hidden = ul_hidden_value = !expanded; }, m(target, anchor) { insert(target, ul, anchor); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].m(ul, null); } current = true; }, p(ctx, dirty) { if (dirty & /*node*/ 1) { each_value = [.../*node*/ ctx[0].children.values()]; let i; for (i = 0; i < each_value.length; i += 1) { const child_ctx = get_each_context$6(ctx, each_value, i); if (each_blocks[i]) { each_blocks[i].p(child_ctx, dirty); transition_in(each_blocks[i], 1); } else { each_blocks[i] = create_each_block$6(child_ctx); each_blocks[i].c(); transition_in(each_blocks[i], 1); each_blocks[i].m(ul, null); } } group_outros(); for (i = each_value.length; i < each_blocks.length; i += 1) { out(i); } check_outros(); } }, i(local) { if (current) return; for (let i = 0; i < each_value.length; i += 1) { transition_in(each_blocks[i]); } current = true; }, o(local) { each_blocks = each_blocks.filter(Boolean); for (let i = 0; i < each_blocks.length; i += 1) { transition_out(each_blocks[i]); } current = false; }, d(detaching) { if (detaching) detach(ul); destroy_each(each_blocks, detaching); } }; } // (75:8) {:else} function create_else_block$1(ctx) { let t; return { c() { t = text("node.name"); }, m(target, anchor) { insert(target, t, anchor); }, p: noop, i: noop, o: noop, d(detaching) { if (detaching) detach(t); } }; } // (73:8) {#if node.children.size} function create_if_block_1(ctx) { let current; const accounts = new Accounts({ props: { node: /*child*/ ctx[10] } }); accounts.$on("drop", /*drop_handler*/ ctx[9]); return { c() { create_component(accounts.$$.fragment); }, m(target, anchor) { mount_component(accounts, target, anchor); current = true; }, p(ctx, dirty) { const accounts_changes = {}; if (dirty & /*node*/ 1) accounts_changes.node = /*child*/ ctx[10]; accounts.$set(accounts_changes); }, i(local) { if (current) return; transition_in(accounts.$$.fragment, local); current = true; }, o(local) { transition_out(accounts.$$.fragment, local); current = false; }, d(detaching) { destroy_component(accounts, detaching); } }; } // (71:4) {#each [...node.children.values()] as child} function create_each_block$6(ctx) { let li; let current_block_type_index; let if_block; let t; let current; const if_block_creators = [create_if_block_1, create_else_block$1]; const if_blocks = []; function select_block_type(ctx, dirty) { if (/*node*/ ctx[0].children.size) return 0; return 1; } current_block_type_index = select_block_type(ctx); if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx); return { c() { li = element("li"); if_block.c(); t = space(); }, m(target, anchor) { insert(target, li, anchor); if_blocks[current_block_type_index].m(li, null); append(li, t); current = true; }, p(ctx, dirty) { let previous_block_index = current_block_type_index; current_block_type_index = select_block_type(ctx); if (current_block_type_index === previous_block_index) { if_blocks[current_block_type_index].p(ctx, dirty); } else { group_outros(); transition_out(if_blocks[previous_block_index], 1, 1, () => { if_blocks[previous_block_index] = null; }); check_outros(); if_block = if_blocks[current_block_type_index]; if (!if_block) { if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx); if_block.c(); } transition_in(if_block, 1); if_block.m(li, t); } }, i(local) { if (current) return; transition_in(if_block); current = true; }, o(local) { transition_out(if_block); current = false; }, d(detaching) { if (detaching) detach(li); if_blocks[current_block_type_index].d(); } }; } function create_fragment$8(ctx) { let t; let if_block1_anchor; let current; let if_block0 = /*node*/ ctx[0].name && create_if_block_2(ctx); let if_block1 = /*node*/ ctx[0].children.size && create_if_block$2(ctx); return { c() { if (if_block0) if_block0.c(); t = space(); if (if_block1) if_block1.c(); if_block1_anchor = empty(); }, m(target, anchor) { if (if_block0) if_block0.m(target, anchor); insert(target, t, anchor); if (if_block1) if_block1.m(target, anchor); insert(target, if_block1_anchor, anchor); current = true; }, p(ctx, [dirty]) { if (/*node*/ ctx[0].name) { if (if_block0) { if_block0.p(ctx, dirty); } else { if_block0 = create_if_block_2(ctx); if_block0.c(); if_block0.m(t.parentNode, t); } } else if (if_block0) { if_block0.d(1); if_block0 = null; } if (/*node*/ ctx[0].children.size) { if (if_block1) { if_block1.p(ctx, dirty); transition_in(if_block1, 1); } else { if_block1 = create_if_block$2(ctx); if_block1.c(); transition_in(if_block1, 1); if_block1.m(if_block1_anchor.parentNode, if_block1_anchor); } } else if (if_block1) { group_outros(); transition_out(if_block1, 1, 1, () => { if_block1 = null; }); check_outros(); } }, i(local) { if (current) return; transition_in(if_block1); current = true; }, o(local) { transition_out(if_block1); current = false; }, d(detaching) { if (if_block0) if_block0.d(detaching); if (detaching) detach(t); if (if_block1) if_block1.d(detaching); if (detaching) detach(if_block1_anchor); } }; } const expanded = true; function instance$8($$self, $$props, $$invalidate) { let $selectedAccount; component_subscribe($$self, selectedAccount, $$value => $$invalidate(2, $selectedAccount = $$value)); let { node } = $$props; let drag = false; function click() { set_store_value(selectedAccount, $selectedAccount = $selectedAccount === node.fullname ? "" : node.fullname); } function dragenter(event) { if (event.dataTransfer.types.includes("fava/filename")) { event.preventDefault(); $$invalidate(1, drag = true); } } const dragover = dragenter; const dispatch = createEventDispatcher(); function drop(event) { const filename = event.dataTransfer.getData("fava/filename"); if (filename) { dispatch("drop", { account: node.fullname, filename }); $$invalidate(1, drag = false); } } const dragleave_handler = () => { $$invalidate(1, drag = false); }; function drop_handler(event) { bubble($$self, event); } $$self.$set = $$props => { if ("node" in $$props) $$invalidate(0, node = $$props.node); }; return [ node, drag, $selectedAccount, click, dragenter, dragover, drop, dispatch, dragleave_handler, drop_handler ]; } class Accounts extends SvelteComponent { constructor(options) { super(); if (!document.getElementById("svelte-g7jz3i-style")) add_css$2(); init(this, options, instance$8, create_fragment$8, safe_not_equal, { node: 0 }); } } /* javascript/documents/Table.svelte generated by Svelte v3.18.2 */ function add_css$3() { var style = element("style"); style.id = "svelte-1fuj8zg-style"; style.textContent = "tr.svelte-1fuj8zg{cursor:pointer}.selected.svelte-1fuj8zg,tr.svelte-1fuj8zg:hover{background-color:var(--color-table-header-background)}"; append(document.head, style); } function get_each_context$7(ctx, list, i) { const child_ctx = ctx.slice(); child_ctx[10] = list[i][0]; child_ctx[11] = list[i][1]; return child_ctx; } function get_each_context_1$3(ctx, list, i) { const child_ctx = ctx.slice(); child_ctx[14] = list[i]; child_ctx[16] = i; return child_ctx; } // (61:6) {#each tableColumns as col, index} function create_each_block_1$3(ctx) { let th; let t0_value = /*col*/ ctx[14].header + ""; let t0; let t1; let th_data_order_value; let dispose; function click_handler(...args) { return /*click_handler*/ ctx[7](/*index*/ ctx[16], ...args); } return { c() { th = element("th"); t0 = text(t0_value); t1 = space(); attr(th, "data-sort", ""); attr(th, "data-order", th_data_order_value = /*index*/ ctx[16] === /*sort*/ ctx[1][0] ? /*sort*/ ctx[1][1] : null); }, m(target, anchor) { insert(target, th, anchor); append(th, t0); append(th, t1); dispose = listen(th, "click", click_handler); }, p(new_ctx, dirty) { ctx = new_ctx; if (dirty & /*sort*/ 2 && th_data_order_value !== (th_data_order_value = /*index*/ ctx[16] === /*sort*/ ctx[1][0] ? /*sort*/ ctx[1][1] : null)) { attr(th, "data-order", th_data_order_value); } }, d(detaching) { if (detaching) detach(th); dispose(); } }; } // (72:4) {#each table as [doc, row]} function create_each_block$7(ctx) { let tr; let td0; let t0_value = /*row*/ ctx[11][0] + ""; let t0; let t1; let td1; let t2_value = /*row*/ ctx[11][1] + ""; let t2; let t3; let tr_title_value; let dispose; function dragstart_handler(...args) { return /*dragstart_handler*/ ctx[8](/*doc*/ ctx[10], ...args); } function click_handler_1(...args) { return /*click_handler_1*/ ctx[9](/*doc*/ ctx[10], ...args); } return { c() { tr = element("tr"); td0 = element("td"); t0 = text(t0_value); t1 = space(); td1 = element("td"); t2 = text(t2_value); t3 = space(); attr(tr, "draggable", "true"); attr(tr, "title", tr_title_value = /*doc*/ ctx[10].filename); attr(tr, "class", "svelte-1fuj8zg"); toggle_class(tr, "selected", /*selected*/ ctx[0] === /*doc*/ ctx[10]); }, m(target, anchor) { insert(target, tr, anchor); append(tr, td0); append(td0, t0); append(tr, t1); append(tr, td1); append(td1, t2); append(tr, t3); dispose = [ listen(tr, "dragstart", dragstart_handler), listen(tr, "click", click_handler_1) ]; }, p(new_ctx, dirty) { ctx = new_ctx; if (dirty & /*table*/ 4 && t0_value !== (t0_value = /*row*/ ctx[11][0] + "")) set_data(t0, t0_value); if (dirty & /*table*/ 4 && t2_value !== (t2_value = /*row*/ ctx[11][1] + "")) set_data(t2, t2_value); if (dirty & /*table*/ 4 && tr_title_value !== (tr_title_value = /*doc*/ ctx[10].filename)) { attr(tr, "title", tr_title_value); } if (dirty & /*selected, table*/ 5) { toggle_class(tr, "selected", /*selected*/ ctx[0] === /*doc*/ ctx[10]); } }, d(detaching) { if (detaching) detach(tr); run_all(dispose); } }; } function create_fragment$9(ctx) { let table_1; let thead; let tr; let t; let tbody; let each_value_1 = /*tableColumns*/ ctx[3]; let each_blocks_1 = []; for (let i = 0; i < each_value_1.length; i += 1) { each_blocks_1[i] = create_each_block_1$3(get_each_context_1$3(ctx, each_value_1, i)); } let each_value = /*table*/ ctx[2]; let each_blocks = []; for (let i = 0; i < each_value.length; i += 1) { each_blocks[i] = create_each_block$7(get_each_context$7(ctx, each_value, i)); } return { c() { table_1 = element("table"); thead = element("thead"); tr = element("tr"); for (let i = 0; i < each_blocks_1.length; i += 1) { each_blocks_1[i].c(); } t = space(); tbody = element("tbody"); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].c(); } attr(tr, "class", "svelte-1fuj8zg"); set_style(table_1, "width", "100%"); }, m(target, anchor) { insert(target, table_1, anchor); append(table_1, thead); append(thead, tr); for (let i = 0; i < each_blocks_1.length; i += 1) { each_blocks_1[i].m(tr, null); } append(table_1, t); append(table_1, tbody); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].m(tbody, null); } }, p(ctx, [dirty]) { if (dirty & /*sort, setSort, tableColumns*/ 26) { each_value_1 = /*tableColumns*/ ctx[3]; let i; for (i = 0; i < each_value_1.length; i += 1) { const child_ctx = get_each_context_1$3(ctx, each_value_1, i); if (each_blocks_1[i]) { each_blocks_1[i].p(child_ctx, dirty); } else { each_blocks_1[i] = create_each_block_1$3(child_ctx); each_blocks_1[i].c(); each_blocks_1[i].m(tr, null); } } for (; i < each_blocks_1.length; i += 1) { each_blocks_1[i].d(1); } each_blocks_1.length = each_value_1.length; } if (dirty & /*table, selected*/ 5) { each_value = /*table*/ ctx[2]; let i; for (i = 0; i < each_value.length; i += 1) { const child_ctx = get_each_context$7(ctx, each_value, i); if (each_blocks[i]) { each_blocks[i].p(child_ctx, dirty); } else { each_blocks[i] = create_each_block$7(child_ctx); each_blocks[i].c(); each_blocks[i].m(tbody, null); } } for (; i < each_blocks.length; i += 1) { each_blocks[i].d(1); } each_blocks.length = each_value.length; } }, i: noop, o: noop, d(detaching) { if (detaching) detach(table_1); destroy_each(each_blocks_1, detaching); destroy_each(each_blocks, detaching); } }; } function name(doc) { const base = basename(doc.filename); if (`${doc.date}` === base.substring(0, 10)) { return base.substring(11); } return base; } function instance$9($$self, $$props, $$invalidate) { let $selectedAccount; component_subscribe($$self, selectedAccount, $$value => $$invalidate(6, $selectedAccount = $$value)); let { data } = $$props; let { selected } = $$props; const tableColumns = [ { header: _("Date"), getter: e => e.date }, { header: _("Name"), getter: e => name(e) } ]; /** Index of the table column and order to sort by */ let sort = [0, "desc"]; function setSort(index) { const [col, order] = sort; if (index === col) { $$invalidate(1, sort = [index, order === "asc" ? "desc" : "asc"]); } else { $$invalidate(1, sort = [index, "asc"]); } } const click_handler = index => setSort(index); const dragstart_handler = (doc, ev) => { ev.dataTransfer.setData("fava/filename", doc.filename); }; const click_handler_1 = doc => { $$invalidate(0, selected = doc); }; $$self.$set = $$props => { if ("data" in $$props) $$invalidate(5, data = $$props.data); if ("selected" in $$props) $$invalidate(0, selected = $$props.selected); }; let table; $$self.$$.update = () => { if ($$self.$$.dirty & /*data, $selectedAccount, sort*/ 98) { $$invalidate(2, table = data.filter(e => e.account.startsWith($selectedAccount)).map(e => [e, tableColumns.map(th => th.getter(e))]).sort(sortFunc("string", sort[1], row => row[1][sort[0]]))); } }; return [ selected, sort, table, tableColumns, setSort, data, $selectedAccount, click_handler, dragstart_handler, click_handler_1 ]; } class Table extends SvelteComponent { constructor(options) { super(); if (!document.getElementById("svelte-1fuj8zg-style")) add_css$3(); init(this, options, instance$9, create_fragment$9, safe_not_equal, { data: 5, selected: 0 }); } } /* javascript/documents/Documents.svelte generated by Svelte v3.18.2 */ function add_css$4() { var style = element("style"); style.id = "svelte-v5zmp9-style"; style.textContent = ".container.svelte-v5zmp9{display:flex;position:fixed;top:var(--header-height);right:0;bottom:0;left:var(--aside-width)}.half-column.svelte-v5zmp9{width:33%;height:100%;overflow:auto;resize:horizontal;border-right:thin solid var(--color-sidebar-border)}"; append(document.head, style); } // (74:0) {#if moving} function create_if_block_1$1(ctx) { let current; const modalbase = new ModalBase({ props: { shown: true, closeHandler: /*func*/ ctx[6], $$slots: { default: [create_default_slot$1] }, $$scope: { ctx } } }); return { c() { create_component(modalbase.$$.fragment); }, m(target, anchor) { mount_component(modalbase, target, anchor); current = true; }, p(ctx, dirty) { const modalbase_changes = {}; if (dirty & /*moving*/ 4) modalbase_changes.closeHandler = /*func*/ ctx[6]; if (dirty & /*$$scope, moving*/ 1028) { modalbase_changes.$$scope = { dirty, ctx }; } modalbase.$set(modalbase_changes); }, i(local) { if (current) return; transition_in(modalbase.$$.fragment, local); current = true; }, o(local) { transition_out(modalbase.$$.fragment, local); current = false; }, d(detaching) { destroy_component(modalbase, detaching); } }; } // (75:2) { moving = null; }}> function create_default_slot$1(ctx) { let div; let h3; let t1; let p0; let code; let t2_value = /*moving*/ ctx[2].filename + ""; let t2; let t3; let p1; let updating_value; let t4; let input; let t5; let button; let current; let dispose; function accountinput_value_binding(value) { /*accountinput_value_binding*/ ctx[7].call(null, value); } let accountinput_props = {}; if (/*moving*/ ctx[2].account !== void 0) { accountinput_props.value = /*moving*/ ctx[2].account; } const accountinput = new AccountInput({ props: accountinput_props }); binding_callbacks.push(() => bind(accountinput, "value", accountinput_value_binding)); return { c() { div = element("div"); h3 = element("h3"); h3.textContent = `${_("Move or rename document")}`; t1 = space(); p0 = element("p"); code = element("code"); t2 = text(t2_value); t3 = space(); p1 = element("p"); create_component(accountinput.$$.fragment); t4 = space(); input = element("input"); t5 = space(); button = element("button"); button.textContent = `${"Move"}`; attr(input, "size", "40"); attr(button, "type", "button"); }, m(target, anchor) { insert(target, div, anchor); append(div, h3); append(div, t1); append(div, p0); append(p0, code); append(code, t2); append(div, t3); append(div, p1); mount_component(accountinput, p1, null); append(p1, t4); append(p1, input); set_input_value(input, /*moving*/ ctx[2].newName); append(p1, t5); append(p1, button); current = true; dispose = [ listen(input, "input", /*input_input_handler*/ ctx[8]), listen(button, "click", /*move*/ ctx[5]) ]; }, p(ctx, dirty) { if ((!current || dirty & /*moving*/ 4) && t2_value !== (t2_value = /*moving*/ ctx[2].filename + "")) set_data(t2, t2_value); const accountinput_changes = {}; if (!updating_value && dirty & /*moving*/ 4) { updating_value = true; accountinput_changes.value = /*moving*/ ctx[2].account; add_flush_callback(() => updating_value = false); } accountinput.$set(accountinput_changes); if (dirty & /*moving*/ 4 && input.value !== /*moving*/ ctx[2].newName) { set_input_value(input, /*moving*/ ctx[2].newName); } }, i(local) { if (current) return; transition_in(accountinput.$$.fragment, local); current = true; }, o(local) { transition_out(accountinput.$$.fragment, local); current = false; }, d(detaching) { if (detaching) detach(div); destroy_component(accountinput); run_all(dispose); } }; } // (101:4) {#if selected} function create_if_block$3(ctx) { let object; let object_title_value; let object_data_value; return { c() { object = element("object"); attr(object, "title", object_title_value = /*selected*/ ctx[1].filename); attr(object, "data", object_data_value = `${favaAPI.baseURL}document/?filename=${/*selected*/ ctx[1].filename}`); set_style(object, "width", "100%"); set_style(object, "height", "100%"); }, m(target, anchor) { insert(target, object, anchor); }, p(ctx, dirty) { if (dirty & /*selected*/ 2 && object_title_value !== (object_title_value = /*selected*/ ctx[1].filename)) { attr(object, "title", object_title_value); } if (dirty & /*selected*/ 2 && object_data_value !== (object_data_value = `${favaAPI.baseURL}document/?filename=${/*selected*/ ctx[1].filename}`)) { attr(object, "data", object_data_value); } }, d(detaching) { if (detaching) detach(object); } }; } function create_fragment$a(ctx) { let t0; let div3; let div0; let t1; let div1; let updating_selected; let t2; let div2; let current; let dispose; let if_block0 = /*moving*/ ctx[2] && create_if_block_1$1(ctx); const accounts = new Accounts({ props: { node: entriesToTree(/*data*/ ctx[0]) } }); accounts.$on("drop", /*drop*/ ctx[4]); function table_selected_binding(value) { /*table_selected_binding*/ ctx[9].call(null, value); } let table_props = { data: /*data*/ ctx[0] }; if (/*selected*/ ctx[1] !== void 0) { table_props.selected = /*selected*/ ctx[1]; } const table = new Table({ props: table_props }); binding_callbacks.push(() => bind(table, "selected", table_selected_binding)); let if_block1 = /*selected*/ ctx[1] && create_if_block$3(ctx); return { c() { if (if_block0) if_block0.c(); t0 = space(); div3 = element("div"); div0 = element("div"); create_component(accounts.$$.fragment); t1 = space(); div1 = element("div"); create_component(table.$$.fragment); t2 = space(); div2 = element("div"); if (if_block1) if_block1.c(); attr(div0, "class", "half-column svelte-v5zmp9"); set_style(div0, "width", "14rem"); attr(div1, "class", "half-column svelte-v5zmp9"); set_style(div2, "flex", "1"); attr(div3, "class", "container svelte-v5zmp9"); }, m(target, anchor) { if (if_block0) if_block0.m(target, anchor); insert(target, t0, anchor); insert(target, div3, anchor); append(div3, div0); mount_component(accounts, div0, null); append(div3, t1); append(div3, div1); mount_component(table, div1, null); append(div3, t2); append(div3, div2); if (if_block1) if_block1.m(div2, null); current = true; dispose = listen(window, "keyup", /*keyup*/ ctx[3]); }, p(ctx, [dirty]) { if (/*moving*/ ctx[2]) { if (if_block0) { if_block0.p(ctx, dirty); transition_in(if_block0, 1); } else { if_block0 = create_if_block_1$1(ctx); if_block0.c(); transition_in(if_block0, 1); if_block0.m(t0.parentNode, t0); } } else if (if_block0) { group_outros(); transition_out(if_block0, 1, 1, () => { if_block0 = null; }); check_outros(); } const accounts_changes = {}; if (dirty & /*data*/ 1) accounts_changes.node = entriesToTree(/*data*/ ctx[0]); accounts.$set(accounts_changes); const table_changes = {}; if (dirty & /*data*/ 1) table_changes.data = /*data*/ ctx[0]; if (!updating_selected && dirty & /*selected*/ 2) { updating_selected = true; table_changes.selected = /*selected*/ ctx[1]; add_flush_callback(() => updating_selected = false); } table.$set(table_changes); if (/*selected*/ ctx[1]) { if (if_block1) { if_block1.p(ctx, dirty); } else { if_block1 = create_if_block$3(ctx); if_block1.c(); if_block1.m(div2, null); } } else if (if_block1) { if_block1.d(1); if_block1 = null; } }, i(local) { if (current) return; transition_in(if_block0); transition_in(accounts.$$.fragment, local); transition_in(table.$$.fragment, local); current = true; }, o(local) { transition_out(if_block0); transition_out(accounts.$$.fragment, local); transition_out(table.$$.fragment, local); current = false; }, d(detaching) { if (if_block0) if_block0.d(detaching); if (detaching) detach(t0); if (detaching) detach(div3); destroy_component(accounts); destroy_component(table); if (if_block1) if_block1.d(); dispose(); } }; } function instance$a($$self, $$props, $$invalidate) { let { data } = $$props; let selected; let moving = null; /** * Rename the selected document with . */ function keyup(ev) { if (ev.key === "F2" && selected) { $$invalidate(2, moving = { account: selected.account, filename: selected.filename, newName: basename(selected.filename) }); } } /** * Move a document to the account it is dropped on. */ function drop(ev) { $$invalidate(2, moving = { account: ev.detail.account, filename: ev.detail.filename, newName: basename(ev.detail.filename) }); } async function move() { const moved = await moveDocument(moving.filename, moving.account, moving.newName); if (moved) { $$invalidate(2, moving = null); router.reload(); } } const func = () => { $$invalidate(2, moving = null); }; function accountinput_value_binding(value) { moving.account = value; $$invalidate(2, moving); } function input_input_handler() { moving.newName = this.value; $$invalidate(2, moving); } function table_selected_binding(value) { selected = value; $$invalidate(1, selected); } $$self.$set = $$props => { if ("data" in $$props) $$invalidate(0, data = $$props.data); }; return [ data, selected, moving, keyup, drop, move, func, accountinput_value_binding, input_input_handler, table_selected_binding ]; } class Documents extends SvelteComponent { constructor(options) { super(); if (!document.getElementById("svelte-v5zmp9-style")) add_css$4(); init(this, options, instance$a, create_fragment$a, safe_not_equal, { data: 0 }); } } function emptyPosting() { return { account: "", amount: "", }; } class Entry { constructor(type) { this.type = type; this.meta = {}; this.date = todayAsString(); } } class Balance extends Entry { constructor() { super("Balance"); this.account = ""; } } class Transaction extends Entry { constructor() { super("Transaction"); this.flag = "*"; this.payee = ""; this.narration = ""; this.postings = [emptyPosting(), emptyPosting()]; } } async function saveEntries(entries) { if (!entries.length) { return; } try { const data = await putAPI("add_entries", { entries }); router.reload(); notify(data); } catch (error) { notify(`Saving failed: ${error}`, "error"); throw error; } } /* javascript/entry-forms/AddMetadataButton.svelte generated by Svelte v3.18.2 */ function create_fragment$b(ctx) { let button; let t; let button_title_value; let dispose; return { c() { button = element("button"); t = text("m"); attr(button, "class", "muted round"); attr(button, "type", "button"); attr(button, "title", button_title_value = _("Add metadata")); }, m(target, anchor) { insert(target, button, anchor); append(button, t); dispose = listen(button, "click", /*addMetadata*/ ctx[0]); }, p: noop, i: noop, o: noop, d(detaching) { if (detaching) detach(button); dispose(); } }; } function instance$b($$self, $$props, $$invalidate) { let { meta } = $$props; function addMetadata() { $$invalidate(1, meta[""] = "", meta); $$invalidate(1, meta); } $$self.$set = $$props => { if ("meta" in $$props) $$invalidate(1, meta = $$props.meta); }; return [addMetadata, meta]; } class AddMetadataButton extends SvelteComponent { constructor(options) { super(); init(this, options, instance$b, create_fragment$b, safe_not_equal, { meta: 1 }); } } /* javascript/entry-forms/EntryMetadata.svelte generated by Svelte v3.18.2 */ function get_each_context$8(ctx, list, i) { const child_ctx = ctx.slice(); child_ctx[8] = list[i]; child_ctx[10] = i; return child_ctx; } // (55:4) {#if i === metakeys.length - 1} function create_if_block$4(ctx) { let button; let t; let button_title_value; let dispose; return { c() { button = element("button"); t = text("+"); attr(button, "class", "muted round add-row"); attr(button, "type", "button"); attr(button, "title", button_title_value = _("Add metadata")); }, m(target, anchor) { insert(target, button, anchor); append(button, t); dispose = listen(button, "click", /*addMetadata*/ ctx[4]); }, p: noop, d(detaching) { if (detaching) detach(button); dispose(); } }; } // (32:0) {#each metakeys as metakey, i} function create_each_block$8(ctx) { let div; let button; let t1; let input0; let input0_placeholder_value; let input0_value_value; let t2; let input1; let input1_placeholder_value; let t3; let t4; let dispose; function click_handler(...args) { return /*click_handler*/ ctx[5](/*metakey*/ ctx[8], ...args); } function change_handler(...args) { return /*change_handler*/ ctx[6](/*metakey*/ ctx[8], ...args); } function input1_input_handler() { /*input1_input_handler*/ ctx[7].call(input1, /*metakey*/ ctx[8]); } let if_block = /*i*/ ctx[10] === /*metakeys*/ ctx[1].length - 1 && create_if_block$4(ctx); return { c() { div = element("div"); button = element("button"); button.textContent = "×"; t1 = space(); input0 = element("input"); t2 = space(); input1 = element("input"); t3 = space(); if (if_block) if_block.c(); t4 = space(); attr(button, "class", "muted round remove-fieldset"); attr(button, "type", "button"); attr(button, "tabindex", "-1"); attr(input0, "type", "text"); attr(input0, "class", "metadata-key"); attr(input0, "placeholder", input0_placeholder_value = _("Key")); input0.value = input0_value_value = /*metakey*/ ctx[8]; input0.required = true; attr(input1, "type", "text"); attr(input1, "class", "metadata-value"); attr(input1, "placeholder", input1_placeholder_value = _("Value")); attr(div, "class", "fieldset metadata"); }, m(target, anchor) { insert(target, div, anchor); append(div, button); append(div, t1); append(div, input0); append(div, t2); append(div, input1); set_input_value(input1, /*meta*/ ctx[0][/*metakey*/ ctx[8]]); append(div, t3); if (if_block) if_block.m(div, null); append(div, t4); dispose = [ listen(button, "click", click_handler), listen(input0, "change", change_handler), listen(input1, "input", input1_input_handler) ]; }, p(new_ctx, dirty) { ctx = new_ctx; if (dirty & /*metakeys*/ 2 && input0_value_value !== (input0_value_value = /*metakey*/ ctx[8]) && input0.value !== input0_value_value) { input0.value = input0_value_value; } if (dirty & /*meta, metakeys*/ 3 && input1.value !== /*meta*/ ctx[0][/*metakey*/ ctx[8]]) { set_input_value(input1, /*meta*/ ctx[0][/*metakey*/ ctx[8]]); } if (/*i*/ ctx[10] === /*metakeys*/ ctx[1].length - 1) { if (if_block) { if_block.p(ctx, dirty); } else { if_block = create_if_block$4(ctx); if_block.c(); if_block.m(div, t4); } } else if (if_block) { if_block.d(1); if_block = null; } }, d(detaching) { if (detaching) detach(div); if (if_block) if_block.d(); run_all(dispose); } }; } function create_fragment$c(ctx) { let each_1_anchor; let each_value = /*metakeys*/ ctx[1]; let each_blocks = []; for (let i = 0; i < each_value.length; i += 1) { each_blocks[i] = create_each_block$8(get_each_context$8(ctx, each_value, i)); } return { c() { for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].c(); } each_1_anchor = empty(); }, m(target, anchor) { for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].m(target, anchor); } insert(target, each_1_anchor, anchor); }, p(ctx, [dirty]) { if (dirty & /*_, addMetadata, metakeys, meta, updateMetakey, removeMetadata*/ 31) { each_value = /*metakeys*/ ctx[1]; let i; for (i = 0; i < each_value.length; i += 1) { const child_ctx = get_each_context$8(ctx, each_value, i); if (each_blocks[i]) { each_blocks[i].p(child_ctx, dirty); } else { each_blocks[i] = create_each_block$8(child_ctx); each_blocks[i].c(); each_blocks[i].m(each_1_anchor.parentNode, each_1_anchor); } } for (; i < each_blocks.length; i += 1) { each_blocks[i].d(1); } each_blocks.length = each_value.length; } }, i: noop, o: noop, d(detaching) { destroy_each(each_blocks, detaching); if (detaching) detach(each_1_anchor); } }; } function instance$c($$self, $$props, $$invalidate) { let { meta } = $$props; function removeMetadata(metakey) { delete meta[metakey]; $$invalidate(0, meta); } function updateMetakey(currentKey, newKey) { $$invalidate(0, meta = Object.keys(meta).reduce( (m, key) => { if (key === currentKey) { m[newKey] = meta[currentKey]; } else { m[key] = meta[key]; } return m; }, {} )); } function addMetadata() { $$invalidate(0, meta[""] = "", meta); $$invalidate(0, meta); } const click_handler = metakey => removeMetadata(metakey); const change_handler = (metakey, event) => { updateMetakey(metakey, event.target.value); }; function input1_input_handler(metakey) { meta[metakey] = this.value; $$invalidate(0, meta); ($$invalidate(1, metakeys), $$invalidate(0, meta)); } $$self.$set = $$props => { if ("meta" in $$props) $$invalidate(0, meta = $$props.meta); }; let metakeys; $$self.$$.update = () => { if ($$self.$$.dirty & /*meta*/ 1) { $$invalidate(1, metakeys = Object.keys(meta).filter(key => !key.startsWith("_") && key !== "filename" && key !== "lineno")); } }; return [ meta, metakeys, removeMetadata, updateMetakey, addMetadata, click_handler, change_handler, input1_input_handler ]; } class EntryMetadata extends SvelteComponent { constructor(options) { super(); init(this, options, instance$c, create_fragment$c, safe_not_equal, { meta: 0 }); } } /* javascript/entry-forms/Posting.svelte generated by Svelte v3.18.2 */ function add_css$5() { var style = element("style"); style.id = "svelte-yf02yw-style"; style.textContent = ".drag.svelte-yf02yw{box-shadow:0 0 5px var(--color-text)}"; append(document.head, style); } function create_fragment$d(ctx) { let div; let button0; let t1; let updating_value; let t2; let input; let input_placeholder_value; let t3; let button1; let t4; let button1_title_value; let current; let dispose; function accountinput_value_binding(value) { /*accountinput_value_binding*/ ctx[12].call(null, value); } let accountinput_props = { suggestions: /*suggestions*/ ctx[1] }; if (/*posting*/ ctx[0].account !== void 0) { accountinput_props.value = /*posting*/ ctx[0].account; } const accountinput = new AccountInput({ props: accountinput_props }); binding_callbacks.push(() => bind(accountinput, "value", accountinput_value_binding)); return { c() { div = element("div"); button0 = element("button"); button0.textContent = "×"; t1 = space(); create_component(accountinput.$$.fragment); t2 = space(); input = element("input"); t3 = space(); button1 = element("button"); t4 = text("+"); attr(button0, "class", "muted round remove-fieldset"); attr(button0, "type", "button"); attr(button0, "tabindex", "-1"); attr(input, "type", "text"); attr(input, "class", "amount"); attr(input, "placeholder", input_placeholder_value = _("Amount")); attr(button1, "class", "muted round add-row"); attr(button1, "type", "button"); attr(button1, "title", button1_title_value = _("Add posting")); attr(div, "class", "fieldset posting svelte-yf02yw"); attr(div, "draggable", /*draggable*/ ctx[3]); toggle_class(div, "drag", /*drag*/ ctx[2]); }, m(target, anchor) { insert(target, div, anchor); append(div, button0); append(div, t1); mount_component(accountinput, div, null); append(div, t2); append(div, input); set_input_value(input, /*posting*/ ctx[0].amount); append(div, t3); append(div, button1); append(button1, t4); current = true; dispose = [ listen(button0, "click", /*click_handler*/ ctx[11]), listen(input, "input", /*input_input_handler*/ ctx[13]), listen(button1, "click", /*click_handler_1*/ ctx[14]), listen(div, "mousemove", /*mousemove*/ ctx[5]), listen(div, "dragstart", /*dragstart*/ ctx[6]), listen(div, "dragenter", /*dragenter*/ ctx[7]), listen(div, "dragover", /*dragenter*/ ctx[7]), listen(div, "dragleave", /*dragleave*/ ctx[8]), listen(div, "drop", prevent_default(/*drop*/ ctx[9])) ]; }, p(ctx, [dirty]) { const accountinput_changes = {}; if (dirty & /*suggestions*/ 2) accountinput_changes.suggestions = /*suggestions*/ ctx[1]; if (!updating_value && dirty & /*posting*/ 1) { updating_value = true; accountinput_changes.value = /*posting*/ ctx[0].account; add_flush_callback(() => updating_value = false); } accountinput.$set(accountinput_changes); if (dirty & /*posting*/ 1 && input.value !== /*posting*/ ctx[0].amount) { set_input_value(input, /*posting*/ ctx[0].amount); } if (!current || dirty & /*draggable*/ 8) { attr(div, "draggable", /*draggable*/ ctx[3]); } if (dirty & /*drag*/ 4) { toggle_class(div, "drag", /*drag*/ ctx[2]); } }, i(local) { if (current) return; transition_in(accountinput.$$.fragment, local); current = true; }, o(local) { transition_out(accountinput.$$.fragment, local); current = false; }, d(detaching) { if (detaching) detach(div); destroy_component(accountinput); run_all(dispose); } }; } function instance$d($$self, $$props, $$invalidate) { let { posting } = $$props; let { index } = $$props; let { suggestions } = $$props; const dispatch = createEventDispatcher(); let drag = false; let draggable = true; function mousemove(event) { $$invalidate(3, draggable = event.target.nodeName !== "INPUT"); } function dragstart(event) { event.dataTransfer.setData("fava/posting", index); } function dragenter(event) { if (event.dataTransfer.types.includes("fava/posting")) { event.preventDefault(); $$invalidate(2, drag = true); } } function dragleave() { $$invalidate(2, drag = false); } function drop(event) { const from = event.dataTransfer.getData("fava/posting"); if (from) { dispatch("move", { from: +from, to: index }); $$invalidate(2, drag = false); } } const click_handler = () => dispatch("remove"); function accountinput_value_binding(value) { posting.account = value; $$invalidate(0, posting); } function input_input_handler() { posting.amount = this.value; $$invalidate(0, posting); } const click_handler_1 = () => dispatch("add"); $$self.$set = $$props => { if ("posting" in $$props) $$invalidate(0, posting = $$props.posting); if ("index" in $$props) $$invalidate(10, index = $$props.index); if ("suggestions" in $$props) $$invalidate(1, suggestions = $$props.suggestions); }; return [ posting, suggestions, drag, draggable, dispatch, mousemove, dragstart, dragenter, dragleave, drop, index, click_handler, accountinput_value_binding, input_input_handler, click_handler_1 ]; } class Posting extends SvelteComponent { constructor(options) { super(); if (!document.getElementById("svelte-yf02yw-style")) add_css$5(); init(this, options, instance$d, create_fragment$d, safe_not_equal, { posting: 0, index: 10, suggestions: 1 }); } } /* javascript/entry-forms/Transaction.svelte generated by Svelte v3.18.2 */ function get_each_context$9(ctx, list, i) { const child_ctx = ctx.slice(); child_ctx[19] = list[i]; child_ctx[20] = list; child_ctx[21] = i; return child_ctx; } // (96:2) {#each entry.postings as posting, index} function create_each_block$9(ctx) { let updating_posting; let current; function postingsvelte_posting_binding(value) { /*postingsvelte_posting_binding*/ ctx[16].call(null, value, /*posting*/ ctx[19], /*each_value*/ ctx[20], /*index*/ ctx[21]); } function remove_handler(...args) { return /*remove_handler*/ ctx[17](/*posting*/ ctx[19], ...args); } let postingsvelte_props = { index: /*index*/ ctx[21], suggestions: /*suggestions*/ ctx[2] }; if (/*posting*/ ctx[19] !== void 0) { postingsvelte_props.posting = /*posting*/ ctx[19]; } const postingsvelte = new Posting({ props: postingsvelte_props }); binding_callbacks.push(() => bind(postingsvelte, "posting", postingsvelte_posting_binding)); postingsvelte.$on("add", /*addPosting*/ ctx[5]); postingsvelte.$on("move", /*movePosting*/ ctx[7]); postingsvelte.$on("remove", remove_handler); return { c() { create_component(postingsvelte.$$.fragment); }, m(target, anchor) { mount_component(postingsvelte, target, anchor); current = true; }, p(new_ctx, dirty) { ctx = new_ctx; const postingsvelte_changes = {}; if (dirty & /*suggestions*/ 4) postingsvelte_changes.suggestions = /*suggestions*/ ctx[2]; if (!updating_posting && dirty & /*entry*/ 1) { updating_posting = true; postingsvelte_changes.posting = /*posting*/ ctx[19]; add_flush_callback(() => updating_posting = false); } postingsvelte.$set(postingsvelte_changes); }, i(local) { if (current) return; transition_in(postingsvelte.$$.fragment, local); current = true; }, o(local) { transition_out(postingsvelte.$$.fragment, local); current = false; }, d(detaching) { destroy_component(postingsvelte, detaching); } }; } function create_fragment$e(ctx) { let div1; let div0; let input0; let t0; let input1; let t1; let label0; let t4; let updating_value; let t5; let label1; let t8; let input2; let input2_placeholder_value; let t9; let updating_meta; let t10; let button; let t11; let button_title_value; let t12; let updating_meta_1; let t13; let current; let dispose; function autocompleteinput_value_binding(value) { /*autocompleteinput_value_binding*/ ctx[12].call(null, value); } let autocompleteinput_props = { className: "payee", placeholder: _("Payee"), suggestions: favaAPI.payees }; if (/*entry*/ ctx[0].payee !== void 0) { autocompleteinput_props.value = /*entry*/ ctx[0].payee; } const autocompleteinput = new AutocompleteInput({ props: autocompleteinput_props }); /*autocompleteinput_binding*/ ctx[11](autocompleteinput); binding_callbacks.push(() => bind(autocompleteinput, "value", autocompleteinput_value_binding)); autocompleteinput.$on("select", /*autocompleteSelectPayee*/ ctx[6]); function addmetadatabutton_meta_binding(value_1) { /*addmetadatabutton_meta_binding*/ ctx[14].call(null, value_1); } let addmetadatabutton_props = {}; if (/*entry*/ ctx[0].meta !== void 0) { addmetadatabutton_props.meta = /*entry*/ ctx[0].meta; } const addmetadatabutton = new AddMetadataButton({ props: addmetadatabutton_props }); binding_callbacks.push(() => bind(addmetadatabutton, "meta", addmetadatabutton_meta_binding)); function entrymetadata_meta_binding(value_2) { /*entrymetadata_meta_binding*/ ctx[15].call(null, value_2); } let entrymetadata_props = {}; if (/*entry*/ ctx[0].meta !== void 0) { entrymetadata_props.meta = /*entry*/ ctx[0].meta; } const entrymetadata = new EntryMetadata({ props: entrymetadata_props }); binding_callbacks.push(() => bind(entrymetadata, "meta", entrymetadata_meta_binding)); let each_value = /*entry*/ ctx[0].postings; let each_blocks = []; for (let i = 0; i < each_value.length; i += 1) { each_blocks[i] = create_each_block$9(get_each_context$9(ctx, each_value, i)); } const out = i => transition_out(each_blocks[i], 1, 1, () => { each_blocks[i] = null; }); return { c() { div1 = element("div"); div0 = element("div"); input0 = element("input"); t0 = space(); input1 = element("input"); t1 = space(); label0 = element("label"); label0.textContent = `${_("Payee")}:`; t4 = space(); create_component(autocompleteinput.$$.fragment); t5 = space(); label1 = element("label"); label1.textContent = `${_("Narration")}:`; t8 = space(); input2 = element("input"); t9 = space(); create_component(addmetadatabutton.$$.fragment); t10 = space(); button = element("button"); t11 = text("p"); t12 = space(); create_component(entrymetadata.$$.fragment); t13 = space(); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].c(); } attr(input0, "type", "date"); input0.required = true; attr(input1, "type", "text"); attr(input1, "name", "flag"); input1.required = true; attr(label0, "for", "payee"); attr(label1, "for", "payee"); attr(input2, "type", "text"); attr(input2, "name", "narration"); attr(input2, "placeholder", input2_placeholder_value = _("Narration")); attr(button, "class", "muted round"); attr(button, "type", "button"); attr(button, "title", button_title_value = _("Add posting")); attr(button, "tabindex", "-1"); attr(div0, "class", "fieldset"); attr(div1, "class", "entry-form transaction"); }, m(target, anchor) { insert(target, div1, anchor); append(div1, div0); append(div0, input0); set_input_value(input0, /*entry*/ ctx[0].date); append(div0, t0); append(div0, input1); set_input_value(input1, /*entry*/ ctx[0].flag); append(div0, t1); append(div0, label0); append(div0, t4); mount_component(autocompleteinput, div0, null); append(div0, t5); append(div0, label1); append(div0, t8); append(div0, input2); set_input_value(input2, /*entry*/ ctx[0].narration); append(div0, t9); mount_component(addmetadatabutton, div0, null); append(div0, t10); append(div0, button); append(button, t11); append(div1, t12); mount_component(entrymetadata, div1, null); append(div1, t13); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].m(div1, null); } /*div1_binding*/ ctx[18](div1); current = true; dispose = [ listen(input0, "input", /*input0_input_handler*/ ctx[9]), listen(input1, "input", /*input1_input_handler*/ ctx[10]), listen(input2, "input", /*input2_input_handler*/ ctx[13]), listen(button, "click", /*addPosting*/ ctx[5]) ]; }, p(ctx, [dirty]) { if (dirty & /*entry*/ 1) { set_input_value(input0, /*entry*/ ctx[0].date); } if (dirty & /*entry*/ 1 && input1.value !== /*entry*/ ctx[0].flag) { set_input_value(input1, /*entry*/ ctx[0].flag); } const autocompleteinput_changes = {}; if (!updating_value && dirty & /*entry*/ 1) { updating_value = true; autocompleteinput_changes.value = /*entry*/ ctx[0].payee; add_flush_callback(() => updating_value = false); } autocompleteinput.$set(autocompleteinput_changes); if (dirty & /*entry*/ 1 && input2.value !== /*entry*/ ctx[0].narration) { set_input_value(input2, /*entry*/ ctx[0].narration); } const addmetadatabutton_changes = {}; if (!updating_meta && dirty & /*entry*/ 1) { updating_meta = true; addmetadatabutton_changes.meta = /*entry*/ ctx[0].meta; add_flush_callback(() => updating_meta = false); } addmetadatabutton.$set(addmetadatabutton_changes); const entrymetadata_changes = {}; if (!updating_meta_1 && dirty & /*entry*/ 1) { updating_meta_1 = true; entrymetadata_changes.meta = /*entry*/ ctx[0].meta; add_flush_callback(() => updating_meta_1 = false); } entrymetadata.$set(entrymetadata_changes); if (dirty & /*suggestions, entry, addPosting, movePosting, removePosting*/ 181) { each_value = /*entry*/ ctx[0].postings; let i; for (i = 0; i < each_value.length; i += 1) { const child_ctx = get_each_context$9(ctx, each_value, i); if (each_blocks[i]) { each_blocks[i].p(child_ctx, dirty); transition_in(each_blocks[i], 1); } else { each_blocks[i] = create_each_block$9(child_ctx); each_blocks[i].c(); transition_in(each_blocks[i], 1); each_blocks[i].m(div1, null); } } group_outros(); for (i = each_value.length; i < each_blocks.length; i += 1) { out(i); } check_outros(); } }, i(local) { if (current) return; transition_in(autocompleteinput.$$.fragment, local); transition_in(addmetadatabutton.$$.fragment, local); transition_in(entrymetadata.$$.fragment, local); for (let i = 0; i < each_value.length; i += 1) { transition_in(each_blocks[i]); } current = true; }, o(local) { transition_out(autocompleteinput.$$.fragment, local); transition_out(addmetadatabutton.$$.fragment, local); transition_out(entrymetadata.$$.fragment, local); each_blocks = each_blocks.filter(Boolean); for (let i = 0; i < each_blocks.length; i += 1) { transition_out(each_blocks[i]); } current = false; }, d(detaching) { if (detaching) detach(div1); /*autocompleteinput_binding*/ ctx[11](null); destroy_component(autocompleteinput); destroy_component(addmetadatabutton); destroy_component(entrymetadata); destroy_each(each_blocks, detaching); /*div1_binding*/ ctx[18](null); run_all(dispose); } }; } const accountCompletionCache = {}; function instance$e($$self, $$props, $$invalidate) { let { entry } = $$props; let focusInput; let suggestions; let el; function focus() { focusInput.focus(); } function removePosting(posting) { $$invalidate(0, entry.postings = entry.postings.filter(p => p !== posting), entry); } async function addPosting() { $$invalidate(0, entry.postings = entry.postings.concat(emptyPosting()), entry); await tick(); const inputs = el.querySelectorAll(".posting .account input"); inputs[inputs.length - 1].focus(); } // Autofill complete transactions. async function autocompleteSelectPayee() { if (entry.narration || !entry.postings.every(p => !p.account)) { return; } const data = await fetchAPI("payee_transaction", { payee: entry.payee }); $$invalidate(0, entry = Object.assign(new Transaction(), data, { date: entry.date })); } function movePosting(ev) { const { from, to } = ev.detail; const moved = entry.postings[from]; entry.postings.splice(from, 1); entry.postings.splice(to, 0, moved); $$invalidate(0, entry); } function input0_input_handler() { entry.date = this.value; $$invalidate(0, entry); } function input1_input_handler() { entry.flag = this.value; $$invalidate(0, entry); } function autocompleteinput_binding($$value) { binding_callbacks[$$value ? "unshift" : "push"](() => { $$invalidate(1, focusInput = $$value); }); } function autocompleteinput_value_binding(value) { entry.payee = value; $$invalidate(0, entry); } function input2_input_handler() { entry.narration = this.value; $$invalidate(0, entry); } function addmetadatabutton_meta_binding(value_1) { entry.meta = value_1; $$invalidate(0, entry); } function entrymetadata_meta_binding(value_2) { entry.meta = value_2; $$invalidate(0, entry); } function postingsvelte_posting_binding(value, posting, each_value, index) { each_value[index] = value; $$invalidate(0, entry); } const remove_handler = posting => removePosting(posting); function div1_binding($$value) { binding_callbacks[$$value ? "unshift" : "push"](() => { $$invalidate(3, el = $$value); }); } $$self.$set = $$props => { if ("entry" in $$props) $$invalidate(0, entry = $$props.entry); }; $$self.$$.update = () => { if ($$self.$$.dirty & /*entry*/ 1) { if (entry.payee) { const { payee } = entry; if (favaAPI.payees.includes(payee)) { if (!accountCompletionCache[payee]) { accountCompletionCache[payee] = fetchAPI("payee_accounts", { payee }); } accountCompletionCache[payee].then(s => { $$invalidate(2, suggestions = s); }); } } } }; return [ entry, focusInput, suggestions, el, removePosting, addPosting, autocompleteSelectPayee, movePosting, focus, input0_input_handler, input1_input_handler, autocompleteinput_binding, autocompleteinput_value_binding, input2_input_handler, addmetadatabutton_meta_binding, entrymetadata_meta_binding, postingsvelte_posting_binding, remove_handler, div1_binding ]; } class Transaction_1 extends SvelteComponent { constructor(options) { super(); init(this, options, instance$e, create_fragment$e, safe_not_equal, { entry: 0, focus: 8 }); } get focus() { return this.$$.ctx[8]; } } /* javascript/entry-forms/Balance.svelte generated by Svelte v3.18.2 */ function create_fragment$f(ctx) { let div1; let div0; let input0; let t0; let h4; let t2; let updating_value; let t3; let input1; let input1_placeholder_value; let t4; let updating_value_1; let t5; let updating_meta; let t6; let updating_meta_1; let current; let dispose; function accountinput_value_binding(value) { /*accountinput_value_binding*/ ctx[2].call(null, value); } let accountinput_props = {}; if (/*entry*/ ctx[0].account !== void 0) { accountinput_props.value = /*entry*/ ctx[0].account; } const accountinput = new AccountInput({ props: accountinput_props }); binding_callbacks.push(() => bind(accountinput, "value", accountinput_value_binding)); function autocompleteinput_value_binding(value_1) { /*autocompleteinput_value_binding*/ ctx[4].call(null, value_1); } let autocompleteinput_props = { className: "currency", placeholder: _("Currency"), suggestions: favaAPI.currencies }; if (/*entry*/ ctx[0].amount.currency !== void 0) { autocompleteinput_props.value = /*entry*/ ctx[0].amount.currency; } const autocompleteinput = new AutocompleteInput({ props: autocompleteinput_props }); binding_callbacks.push(() => bind(autocompleteinput, "value", autocompleteinput_value_binding)); function addmetadatabutton_meta_binding(value_2) { /*addmetadatabutton_meta_binding*/ ctx[5].call(null, value_2); } let addmetadatabutton_props = {}; if (/*entry*/ ctx[0].meta !== void 0) { addmetadatabutton_props.meta = /*entry*/ ctx[0].meta; } const addmetadatabutton = new AddMetadataButton({ props: addmetadatabutton_props }); binding_callbacks.push(() => bind(addmetadatabutton, "meta", addmetadatabutton_meta_binding)); function entrymetadata_meta_binding(value_3) { /*entrymetadata_meta_binding*/ ctx[6].call(null, value_3); } let entrymetadata_props = {}; if (/*entry*/ ctx[0].meta !== void 0) { entrymetadata_props.meta = /*entry*/ ctx[0].meta; } const entrymetadata = new EntryMetadata({ props: entrymetadata_props }); binding_callbacks.push(() => bind(entrymetadata, "meta", entrymetadata_meta_binding)); return { c() { div1 = element("div"); div0 = element("div"); input0 = element("input"); t0 = space(); h4 = element("h4"); h4.textContent = `${_("Balance")}`; t2 = space(); create_component(accountinput.$$.fragment); t3 = space(); input1 = element("input"); t4 = space(); create_component(autocompleteinput.$$.fragment); t5 = space(); create_component(addmetadatabutton.$$.fragment); t6 = space(); create_component(entrymetadata.$$.fragment); attr(input0, "type", "date"); input0.required = true; attr(input1, "type", "tel"); attr(input1, "class", "number"); attr(input1, "pattern", "-?[0-9.,]*"); attr(input1, "placeholder", input1_placeholder_value = _("Number")); attr(input1, "size", "10"); attr(div0, "class", "fieldset"); attr(div1, "class", "entry-form balance"); }, m(target, anchor) { insert(target, div1, anchor); append(div1, div0); append(div0, input0); set_input_value(input0, /*entry*/ ctx[0].date); append(div0, t0); append(div0, h4); append(div0, t2); mount_component(accountinput, div0, null); append(div0, t3); append(div0, input1); set_input_value(input1, /*entry*/ ctx[0].amount.number); append(div0, t4); mount_component(autocompleteinput, div0, null); append(div0, t5); mount_component(addmetadatabutton, div0, null); append(div1, t6); mount_component(entrymetadata, div1, null); current = true; dispose = [ listen(input0, "input", /*input0_input_handler*/ ctx[1]), listen(input1, "input", /*input1_input_handler*/ ctx[3]) ]; }, p(ctx, [dirty]) { if (dirty & /*entry*/ 1) { set_input_value(input0, /*entry*/ ctx[0].date); } const accountinput_changes = {}; if (!updating_value && dirty & /*entry*/ 1) { updating_value = true; accountinput_changes.value = /*entry*/ ctx[0].account; add_flush_callback(() => updating_value = false); } accountinput.$set(accountinput_changes); if (dirty & /*entry*/ 1) { set_input_value(input1, /*entry*/ ctx[0].amount.number); } const autocompleteinput_changes = {}; if (!updating_value_1 && dirty & /*entry*/ 1) { updating_value_1 = true; autocompleteinput_changes.value = /*entry*/ ctx[0].amount.currency; add_flush_callback(() => updating_value_1 = false); } autocompleteinput.$set(autocompleteinput_changes); const addmetadatabutton_changes = {}; if (!updating_meta && dirty & /*entry*/ 1) { updating_meta = true; addmetadatabutton_changes.meta = /*entry*/ ctx[0].meta; add_flush_callback(() => updating_meta = false); } addmetadatabutton.$set(addmetadatabutton_changes); const entrymetadata_changes = {}; if (!updating_meta_1 && dirty & /*entry*/ 1) { updating_meta_1 = true; entrymetadata_changes.meta = /*entry*/ ctx[0].meta; add_flush_callback(() => updating_meta_1 = false); } entrymetadata.$set(entrymetadata_changes); }, i(local) { if (current) return; transition_in(accountinput.$$.fragment, local); transition_in(autocompleteinput.$$.fragment, local); transition_in(addmetadatabutton.$$.fragment, local); transition_in(entrymetadata.$$.fragment, local); current = true; }, o(local) { transition_out(accountinput.$$.fragment, local); transition_out(autocompleteinput.$$.fragment, local); transition_out(addmetadatabutton.$$.fragment, local); transition_out(entrymetadata.$$.fragment, local); current = false; }, d(detaching) { if (detaching) detach(div1); destroy_component(accountinput); destroy_component(autocompleteinput); destroy_component(addmetadatabutton); destroy_component(entrymetadata); run_all(dispose); } }; } function instance$f($$self, $$props, $$invalidate) { let { entry } = $$props; function input0_input_handler() { entry.date = this.value; $$invalidate(0, entry); } function accountinput_value_binding(value) { entry.account = value; $$invalidate(0, entry); } function input1_input_handler() { entry.amount.number = this.value; $$invalidate(0, entry); } function autocompleteinput_value_binding(value_1) { entry.amount.currency = value_1; $$invalidate(0, entry); } function addmetadatabutton_meta_binding(value_2) { entry.meta = value_2; $$invalidate(0, entry); } function entrymetadata_meta_binding(value_3) { entry.meta = value_3; $$invalidate(0, entry); } $$self.$set = $$props => { if ("entry" in $$props) $$invalidate(0, entry = $$props.entry); }; $$self.$$.update = () => { if ($$self.$$.dirty & /*entry*/ 1) { if (entry && !entry.amount) { $$invalidate(0, entry.amount = { number: "", currency: "" }, entry); } } }; return [ entry, input0_input_handler, accountinput_value_binding, input1_input_handler, autocompleteinput_value_binding, addmetadatabutton_meta_binding, entrymetadata_meta_binding ]; } class Balance$1 extends SvelteComponent { constructor(options) { super(); init(this, options, instance$f, create_fragment$f, safe_not_equal, { entry: 0 }); } } /* javascript/modals/AddEntry.svelte generated by Svelte v3.18.2 */ function get_each_context$a(ctx, list, i) { const child_ctx = ctx.slice(); child_ctx[12] = list[i][0]; child_ctx[13] = list[i][1]; child_ctx[14] = list[i][2]; return child_ctx; } // (56:6) {#each entryTypes as [name, Cls, component]} function create_each_block$a(ctx) { let button; let t0_value = /*name*/ ctx[12] + ""; let t0; let t1; let dispose; function click_handler(...args) { return /*click_handler*/ ctx[9](/*Cls*/ ctx[13], ...args); } return { c() { button = element("button"); t0 = text(t0_value); t1 = space(); attr(button, "type", "button"); toggle_class(button, "muted", !(/*entry*/ ctx[0] instanceof /*Cls*/ ctx[13])); }, m(target, anchor) { insert(target, button, anchor); append(button, t0); append(button, t1); dispose = listen(button, "click", click_handler); }, p(new_ctx, dirty) { ctx = new_ctx; if (dirty & /*entry, entryTypes*/ 17) { toggle_class(button, "muted", !(/*entry*/ ctx[0] instanceof /*Cls*/ ctx[13])); } }, d(detaching) { if (detaching) detach(button); dispose(); } }; } // (52:0) function create_default_slot$2(ctx) { let form; let h3; let t0_value = _("Add") + ""; let t0; let t1; let t2; let updating_entry; let t3; let div; let span; let t4; let button0; let t6; let button1; let current; let dispose; let each_value = /*entryTypes*/ ctx[4]; let each_blocks = []; for (let i = 0; i < each_value.length; i += 1) { each_blocks[i] = create_each_block$a(get_each_context$a(ctx, each_value, i)); } function switch_instance_entry_binding(value) { /*switch_instance_entry_binding*/ ctx[11].call(null, value); } var switch_value = /*svelteComponent*/ ctx[2]; function switch_props(ctx) { let switch_instance_props = {}; if (/*entry*/ ctx[0] !== void 0) { switch_instance_props.entry = /*entry*/ ctx[0]; } return { props: switch_instance_props }; } if (switch_value) { var switch_instance = new switch_value(switch_props(ctx)); /*switch_instance_binding*/ ctx[10](switch_instance); binding_callbacks.push(() => bind(switch_instance, "entry", switch_instance_entry_binding)); } return { c() { form = element("form"); h3 = element("h3"); t0 = text(t0_value); t1 = space(); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].c(); } t2 = space(); if (switch_instance) create_component(switch_instance.$$.fragment); t3 = space(); div = element("div"); span = element("span"); t4 = space(); button0 = element("button"); button0.textContent = `${_("Save and add new")}`; t6 = space(); button1 = element("button"); button1.textContent = `${_("Save")}`; attr(span, "class", "spacer"); attr(button0, "type", "submit"); attr(button0, "class", "muted"); attr(button1, "type", "submit"); attr(div, "class", "fieldset"); }, m(target, anchor) { insert(target, form, anchor); append(form, h3); append(h3, t0); append(h3, t1); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].m(h3, null); } append(form, t2); if (switch_instance) { mount_component(switch_instance, form, null); } append(form, t3); append(form, div); append(div, span); append(div, t4); append(div, button0); append(div, t6); append(div, button1); current = true; dispose = [ listen(button0, "click", prevent_default(/*submitAndNew*/ ctx[5])), listen(form, "submit", prevent_default(/*submit*/ ctx[6])) ]; }, p(ctx, dirty) { if (dirty & /*entry, entryTypes*/ 17) { each_value = /*entryTypes*/ ctx[4]; let i; for (i = 0; i < each_value.length; i += 1) { const child_ctx = get_each_context$a(ctx, each_value, i); if (each_blocks[i]) { each_blocks[i].p(child_ctx, dirty); } else { each_blocks[i] = create_each_block$a(child_ctx); each_blocks[i].c(); each_blocks[i].m(h3, null); } } for (; i < each_blocks.length; i += 1) { each_blocks[i].d(1); } each_blocks.length = each_value.length; } const switch_instance_changes = {}; if (!updating_entry && dirty & /*entry*/ 1) { updating_entry = true; switch_instance_changes.entry = /*entry*/ ctx[0]; add_flush_callback(() => updating_entry = false); } if (switch_value !== (switch_value = /*svelteComponent*/ ctx[2])) { if (switch_instance) { group_outros(); const old_component = switch_instance; transition_out(old_component.$$.fragment, 1, 0, () => { destroy_component(old_component, 1); }); check_outros(); } if (switch_value) { switch_instance = new switch_value(switch_props(ctx)); /*switch_instance_binding*/ ctx[10](switch_instance); binding_callbacks.push(() => bind(switch_instance, "entry", switch_instance_entry_binding)); create_component(switch_instance.$$.fragment); transition_in(switch_instance.$$.fragment, 1); mount_component(switch_instance, form, t3); } else { switch_instance = null; } } else if (switch_value) { switch_instance.$set(switch_instance_changes); } }, i(local) { if (current) return; if (switch_instance) transition_in(switch_instance.$$.fragment, local); current = true; }, o(local) { if (switch_instance) transition_out(switch_instance.$$.fragment, local); current = false; }, d(detaching) { if (detaching) detach(form); destroy_each(each_blocks, detaching); /*switch_instance_binding*/ ctx[10](null); if (switch_instance) destroy_component(switch_instance); run_all(dispose); } }; } function create_fragment$g(ctx) { let current; const modalbase = new ModalBase({ props: { shown: /*shown*/ ctx[3], $$slots: { default: [create_default_slot$2] }, $$scope: { ctx } } }); return { c() { create_component(modalbase.$$.fragment); }, m(target, anchor) { mount_component(modalbase, target, anchor); current = true; }, p(ctx, [dirty]) { const modalbase_changes = {}; if (dirty & /*shown*/ 8) modalbase_changes.shown = /*shown*/ ctx[3]; if (dirty & /*$$scope, svelteComponent, entryComponent, entry*/ 131079) { modalbase_changes.$$scope = { dirty, ctx }; } modalbase.$set(modalbase_changes); }, i(local) { if (current) return; transition_in(modalbase.$$.fragment, local); current = true; }, o(local) { transition_out(modalbase.$$.fragment, local); current = false; }, d(detaching) { destroy_component(modalbase, detaching); } }; } function instance$g($$self, $$props, $$invalidate) { let $urlHash; component_subscribe($$self, urlHash, $$value => $$invalidate(7, $urlHash = $$value)); const entryTypes = [[_("Transaction"), Transaction], [_("Balance"), Balance]]; let entry = new Transaction(); let entryComponent; async function focus() { await tick(); if (entryComponent.focus) { entryComponent.focus(); } } async function submitAndNew(event) { if (event.target.form.reportValidity()) { await saveEntries([entry]); $$invalidate(0, entry = new entry.constructor()); focus(); } } async function submit() { await saveEntries([entry]); $$invalidate(0, entry = new entry.constructor()); closeOverlay(); } const click_handler = Cls => { $$invalidate(0, entry = new Cls()); }; function switch_instance_binding($$value) { binding_callbacks[$$value ? "unshift" : "push"](() => { $$invalidate(1, entryComponent = $$value); }); } function switch_instance_entry_binding(value) { entry = value; $$invalidate(0, entry); } let svelteComponent; let shown; $$self.$$.update = () => { if ($$self.$$.dirty & /*entry*/ 1) { $$invalidate(2, svelteComponent = ({ Transaction: Transaction_1, Balance: Balance$1 })[entry.constructor.name]); } if ($$self.$$.dirty & /*$urlHash*/ 128) { $$invalidate(3, shown = $urlHash === "add-transaction"); } if ($$self.$$.dirty & /*shown*/ 8) { if (shown) { focus(); } } }; return [ entry, entryComponent, svelteComponent, shown, entryTypes, submitAndNew, submit, $urlHash, focus, click_handler, switch_instance_binding, switch_instance_entry_binding ]; } class AddEntry extends SvelteComponent { constructor(options) { super(); init(this, options, instance$g, create_fragment$g, safe_not_equal, {}); } } /* javascript/modals/Context.svelte generated by Svelte v3.18.2 */ function create_catch_block(ctx) { let t; return { c() { t = text("Loading entry context failed."); }, m(target, anchor) { insert(target, t, anchor); }, p: noop, d(detaching) { if (detaching) detach(t); } }; } // (38:4) {:then html} function create_then_block(ctx) { let html_tag; let raw_value = /*html*/ ctx[6] + ""; return { c() { html_tag = new HtmlTag(raw_value, null); }, m(target, anchor) { html_tag.m(target, anchor); }, p(ctx, dirty) { if (dirty & /*content*/ 4 && raw_value !== (raw_value = /*html*/ ctx[6] + "")) html_tag.p(raw_value); }, d(detaching) { if (detaching) html_tag.d(); } }; } // (36:20) Loading entry context... {:then html} function create_pending_block(ctx) { let t; return { c() { t = text("Loading entry context..."); }, m(target, anchor) { insert(target, t, anchor); }, p: noop, d(detaching) { if (detaching) detach(t); } }; } // (34:0) function create_default_slot$3(ctx) { let div_1; let promise; let info = { ctx, current: null, token: null, pending: create_pending_block, then: create_then_block, catch: create_catch_block, value: 6 }; handle_promise(promise = /*content*/ ctx[2], info); return { c() { div_1 = element("div"); info.block.c(); attr(div_1, "class", "content"); }, m(target, anchor) { insert(target, div_1, anchor); info.block.m(div_1, info.anchor = null); info.mount = () => div_1; info.anchor = null; /*div_1_binding*/ ctx[5](div_1); }, p(new_ctx, dirty) { ctx = new_ctx; info.ctx = ctx; if (dirty & /*content*/ 4 && promise !== (promise = /*content*/ ctx[2]) && handle_promise(promise, info)) ; else { const child_ctx = ctx.slice(); child_ctx[6] = info.resolved; info.block.p(child_ctx, dirty); } }, d(detaching) { if (detaching) detach(div_1); info.block.d(); info.token = null; info = null; /*div_1_binding*/ ctx[5](null); } }; } function create_fragment$h(ctx) { let current; const modalbase = new ModalBase({ props: { shown: /*shown*/ ctx[1], $$slots: { default: [create_default_slot$3] }, $$scope: { ctx } } }); return { c() { create_component(modalbase.$$.fragment); }, m(target, anchor) { mount_component(modalbase, target, anchor); current = true; }, p(ctx, [dirty]) { const modalbase_changes = {}; if (dirty & /*shown*/ 2) modalbase_changes.shown = /*shown*/ ctx[1]; if (dirty & /*$$scope, div, content*/ 133) { modalbase_changes.$$scope = { dirty, ctx }; } modalbase.$set(modalbase_changes); }, i(local) { if (current) return; transition_in(modalbase.$$.fragment, local); current = true; }, o(local) { transition_out(modalbase.$$.fragment, local); current = false; }, d(detaching) { destroy_component(modalbase, detaching); } }; } function instance$h($$self, $$props, $$invalidate) { let $urlHash; component_subscribe($$self, urlHash, $$value => $$invalidate(3, $urlHash = $$value)); let div; onMount(() => { delegate(div, "click", ".toggle-box-header", event => { event.target.closest(".toggle-box").classList.toggle("toggled"); }); }); afterUpdate(async () => { if (!content) { return; } await content; initSourceEditor("#source-slice-editor"); }); function div_1_binding($$value) { binding_callbacks[$$value ? "unshift" : "push"](() => { $$invalidate(0, div = $$value); }); } let shown; let entryHash; let content; $$self.$$.update = () => { if ($$self.$$.dirty & /*$urlHash*/ 8) { $$invalidate(1, shown = $urlHash.startsWith("context")); } if ($$self.$$.dirty & /*shown, $urlHash*/ 10) { $$invalidate(4, entryHash = shown ? $urlHash.slice(8) : ""); } if ($$self.$$.dirty & /*shown, entryHash*/ 18) { $$invalidate(2, content = !shown ? "" : fetch(`${favaAPI.baseURL}_context/?entry_hash=${entryHash}`).then(handleText)); } }; return [div, shown, content, $urlHash, entryHash, div_1_binding]; } class Context extends SvelteComponent { constructor(options) { super(); init(this, options, instance$h, create_fragment$h, safe_not_equal, {}); } } /* * File uploads via Drag and Drop on elements with class "droptarget" * and attribute "data-account-name" */ function dragover(event, closestTarget) { closestTarget.classList.add("dragover"); event.preventDefault(); } delegate(document, "dragenter", ".droptarget", dragover); delegate(document, "dragover", ".droptarget", dragover); function dragleave(event, closestTarget) { closestTarget.classList.remove("dragover"); event.preventDefault(); } delegate(document, "dragleave", ".droptarget", dragleave); /* Stores that the Svelte component accesses. */ const account = writable(""); const hash = writable(""); const files = writable([]); function drop(event, target) { target.classList.remove("dragover"); event.preventDefault(); event.stopPropagation(); if (!event.dataTransfer || !event.dataTransfer.files.length) { return; } if (!favaAPI.options.documents.length) { notify(_('You need to set the "documents" Beancount option for file uploads.'), "error"); return; } const dateAttribute = target.getAttribute("data-entry-date"); const entryDate = dateAttribute || todayAsString(); account.set(target.getAttribute("data-account-name") || ""); hash.set(target.getAttribute("data-entry") || ""); const uploadedFiles = []; for (const dataTransferFile of event.dataTransfer.files) { let { name } = dataTransferFile; if (!/^\d{4}-\d{2}-\d{2}/.test(name)) { name = `${entryDate} ${name}`; } uploadedFiles.push({ dataTransferFile, name, }); } files.set(uploadedFiles); } delegate(document, "drop", ".droptarget", drop); /* javascript/modals/DocumentUpload.svelte generated by Svelte v3.18.2 */ function get_each_context$b(ctx, list, i) { const child_ctx = ctx.slice(); child_ctx[10] = list[i]; return child_ctx; } function get_each_context_1$4(ctx, list, i) { const child_ctx = ctx.slice(); child_ctx[13] = list[i]; child_ctx[14] = list; child_ctx[15] = i; return child_ctx; } // (50:4) {#each $files as file} function create_each_block_1$4(ctx) { let div; let input; let dispose; function input_input_handler() { /*input_input_handler*/ ctx[7].call(input, /*file*/ ctx[13]); } return { c() { div = element("div"); input = element("input"); attr(div, "class", "fieldset"); }, m(target, anchor) { insert(target, div, anchor); append(div, input); set_input_value(input, /*file*/ ctx[13].name); dispose = listen(input, "input", input_input_handler); }, p(new_ctx, dirty) { ctx = new_ctx; if (dirty & /*$files*/ 4 && input.value !== /*file*/ ctx[13].name) { set_input_value(input, /*file*/ ctx[13].name); } }, d(detaching) { if (detaching) detach(div); dispose(); } }; } // (59:10) {#each favaAPI.options.documents as folder} function create_each_block$b(ctx) { let option; let t_value = /*folder*/ ctx[10] + ""; let t; let option_value_value; return { c() { option = element("option"); t = text(t_value); option.__value = option_value_value = /*folder*/ ctx[10]; option.value = option.__value; }, m(target, anchor) { insert(target, option, anchor); append(option, t); }, p: noop, d(detaching) { if (detaching) detach(option); } }; } // (47:0) function create_default_slot$4(ctx) { let form_1; let h3; let t2; let t3; let div0; let label0; let t4_value = _("Documents folder") + ""; let t4; let t5; let select; let t6; let div1; let label1; let t7_value = _("Account") + ""; let t7; let t8; let updating_value; let t9; let input; let t10; let button; let current; let dispose; let each_value_1 = /*$files*/ ctx[2]; let each_blocks_1 = []; for (let i = 0; i < each_value_1.length; i += 1) { each_blocks_1[i] = create_each_block_1$4(get_each_context_1$4(ctx, each_value_1, i)); } let each_value = favaAPI.options.documents; let each_blocks = []; for (let i = 0; i < each_value.length; i += 1) { each_blocks[i] = create_each_block$b(get_each_context$b(ctx, each_value, i)); } function accountinput_value_binding(value) { /*accountinput_value_binding*/ ctx[8].call(null, value); } let accountinput_props = {}; if (/*$account*/ ctx[3] !== void 0) { accountinput_props.value = /*$account*/ ctx[3]; } const accountinput = new AccountInput({ props: accountinput_props }); binding_callbacks.push(() => bind(accountinput, "value", accountinput_value_binding)); return { c() { form_1 = element("form"); h3 = element("h3"); h3.textContent = `${_("Upload file(s)")}:`; t2 = space(); for (let i = 0; i < each_blocks_1.length; i += 1) { each_blocks_1[i].c(); } t3 = space(); div0 = element("div"); label0 = element("label"); t4 = text(t4_value); t5 = text(":\n "); select = element("select"); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].c(); } t6 = space(); div1 = element("div"); label1 = element("label"); t7 = text(t7_value); t8 = text(":\n "); create_component(accountinput.$$.fragment); t9 = space(); input = element("input"); t10 = space(); button = element("button"); button.textContent = `${_("Upload")}`; attr(select, "name", "folder"); attr(div0, "class", "fieldset"); attr(input, "type", "hidden"); attr(input, "name", "hash"); input.value = /*$hash*/ ctx[4]; attr(div1, "class", "fieldset"); attr(button, "type", "submit"); }, m(target, anchor) { insert(target, form_1, anchor); append(form_1, h3); append(form_1, t2); for (let i = 0; i < each_blocks_1.length; i += 1) { each_blocks_1[i].m(form_1, null); } append(form_1, t3); append(form_1, div0); append(div0, label0); append(label0, t4); append(label0, t5); append(label0, select); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].m(select, null); } append(form_1, t6); append(form_1, div1); append(div1, label1); append(label1, t7); append(label1, t8); mount_component(accountinput, label1, null); append(div1, t9); append(div1, input); append(form_1, t10); append(form_1, button); /*form_1_binding*/ ctx[9](form_1); current = true; dispose = listen(form_1, "submit", prevent_default(/*submit*/ ctx[5])); }, p(ctx, dirty) { if (dirty & /*$files*/ 4) { each_value_1 = /*$files*/ ctx[2]; let i; for (i = 0; i < each_value_1.length; i += 1) { const child_ctx = get_each_context_1$4(ctx, each_value_1, i); if (each_blocks_1[i]) { each_blocks_1[i].p(child_ctx, dirty); } else { each_blocks_1[i] = create_each_block_1$4(child_ctx); each_blocks_1[i].c(); each_blocks_1[i].m(form_1, t3); } } for (; i < each_blocks_1.length; i += 1) { each_blocks_1[i].d(1); } each_blocks_1.length = each_value_1.length; } if (dirty & /*favaAPI*/ 0) { each_value = favaAPI.options.documents; let i; for (i = 0; i < each_value.length; i += 1) { const child_ctx = get_each_context$b(ctx, each_value, i); if (each_blocks[i]) { each_blocks[i].p(child_ctx, dirty); } else { each_blocks[i] = create_each_block$b(child_ctx); each_blocks[i].c(); each_blocks[i].m(select, null); } } for (; i < each_blocks.length; i += 1) { each_blocks[i].d(1); } each_blocks.length = each_value.length; } const accountinput_changes = {}; if (!updating_value && dirty & /*$account*/ 8) { updating_value = true; accountinput_changes.value = /*$account*/ ctx[3]; add_flush_callback(() => updating_value = false); } accountinput.$set(accountinput_changes); if (!current || dirty & /*$hash*/ 16) { input.value = /*$hash*/ ctx[4]; } }, i(local) { if (current) return; transition_in(accountinput.$$.fragment, local); current = true; }, o(local) { transition_out(accountinput.$$.fragment, local); current = false; }, d(detaching) { if (detaching) detach(form_1); destroy_each(each_blocks_1, detaching); destroy_each(each_blocks, detaching); destroy_component(accountinput); /*form_1_binding*/ ctx[9](null); dispose(); } }; } function create_fragment$i(ctx) { let current; const modalbase = new ModalBase({ props: { shown: /*shown*/ ctx[1], closeHandler: /*closeHandler*/ ctx[6], $$slots: { default: [create_default_slot$4] }, $$scope: { ctx } } }); return { c() { create_component(modalbase.$$.fragment); }, m(target, anchor) { mount_component(modalbase, target, anchor); current = true; }, p(ctx, [dirty]) { const modalbase_changes = {}; if (dirty & /*shown*/ 2) modalbase_changes.shown = /*shown*/ ctx[1]; if (dirty & /*$$scope, form, $hash, $account, $files*/ 65565) { modalbase_changes.$$scope = { dirty, ctx }; } modalbase.$set(modalbase_changes); }, i(local) { if (current) return; transition_in(modalbase.$$.fragment, local); current = true; }, o(local) { transition_out(modalbase.$$.fragment, local); current = false; }, d(detaching) { destroy_component(modalbase, detaching); } }; } function instance$i($$self, $$props, $$invalidate) { let $files; let $account; let $hash; component_subscribe($$self, files, $$value => $$invalidate(2, $files = $$value)); component_subscribe($$self, account, $$value => $$invalidate(3, $account = $$value)); component_subscribe($$self, hash, $$value => $$invalidate(4, $hash = $$value)); let form; async function submit() { await Promise.all($files.map(({ dataTransferFile, name }) => { const formData = new FormData(form); formData.append("account", $account); formData.append("file", dataTransferFile, name); return fetch(`${favaAPI.baseURL}api/add-document/`, { method: "PUT", body: formData }).then(handleJSON).then( response => { notify(response.data); }, error => { notify(`Upload error: ${error}`, "error"); } ); })); set_store_value(files, $files = []); set_store_value(account, $account = ""); set_store_value(hash, $hash = ""); router.reload(); } function closeHandler() { $$invalidate(1, shown = false); set_store_value(files, $files = []); } function input_input_handler(file) { file.name = this.value; files.set($files); } function accountinput_value_binding(value) { $account = value; account.set($account); } function form_1_binding($$value) { binding_callbacks[$$value ? "unshift" : "push"](() => { $$invalidate(0, form = $$value); }); } let shown; $$self.$$.update = () => { if ($$self.$$.dirty & /*$files*/ 4) { $$invalidate(1, shown = $files.length); } }; return [ form, shown, $files, $account, $hash, submit, closeHandler, input_input_handler, accountinput_value_binding, form_1_binding ]; } class DocumentUpload extends SvelteComponent { constructor(options) { super(); init(this, options, instance$i, create_fragment$i, safe_not_equal, {}); } } /* javascript/modals/Export.svelte generated by Svelte v3.18.2 */ function create_default_slot$5(ctx) { let div; return { c() { div = element("div"); }, m(target, anchor) { insert(target, div, anchor); div.innerHTML = /*content*/ ctx[0]; }, p(ctx, dirty) { if (dirty & /*content*/ 1) div.innerHTML = /*content*/ ctx[0]; }, d(detaching) { if (detaching) detach(div); } }; } function create_fragment$j(ctx) { let current; const modalbase = new ModalBase({ props: { shown: /*shown*/ ctx[1], $$slots: { default: [create_default_slot$5] }, $$scope: { ctx } } }); return { c() { create_component(modalbase.$$.fragment); }, m(target, anchor) { mount_component(modalbase, target, anchor); current = true; }, p(ctx, [dirty]) { const modalbase_changes = {}; if (dirty & /*shown*/ 2) modalbase_changes.shown = /*shown*/ ctx[1]; if (dirty & /*$$scope, content*/ 9) { modalbase_changes.$$scope = { dirty, ctx }; } modalbase.$set(modalbase_changes); }, i(local) { if (current) return; transition_in(modalbase.$$.fragment, local); current = true; }, o(local) { transition_out(modalbase.$$.fragment, local); current = false; }, d(detaching) { destroy_component(modalbase, detaching); } }; } function instance$j($$self, $$props, $$invalidate) { let $urlHash; component_subscribe($$self, urlHash, $$value => $$invalidate(2, $urlHash = $$value)); let content = ""; onMount(() => { const template = document.querySelector("#export-overlay-content"); $$invalidate(0, content = template.innerHTML); }); let shown; $$self.$$.update = () => { if ($$self.$$.dirty & /*$urlHash*/ 4) { $$invalidate(1, shown = $urlHash === "export"); } }; return [content, shown]; } class Export extends SvelteComponent { constructor(options) { super(); init(this, options, instance$j, create_fragment$j, safe_not_equal, {}); } } /* javascript/entry-forms/Note.svelte generated by Svelte v3.18.2 */ function create_fragment$k(ctx) { let div2; let div0; let input; let t0; let h4; let t2; let updating_value; let t3; let updating_meta; let t4; let div1; let textarea; let t5; let updating_meta_1; let current; let dispose; function accountinput_value_binding(value) { /*accountinput_value_binding*/ ctx[2].call(null, value); } let accountinput_props = {}; if (/*entry*/ ctx[0].account !== void 0) { accountinput_props.value = /*entry*/ ctx[0].account; } const accountinput = new AccountInput({ props: accountinput_props }); binding_callbacks.push(() => bind(accountinput, "value", accountinput_value_binding)); function addmetadatabutton_meta_binding(value_1) { /*addmetadatabutton_meta_binding*/ ctx[3].call(null, value_1); } let addmetadatabutton_props = {}; if (/*entry*/ ctx[0].meta !== void 0) { addmetadatabutton_props.meta = /*entry*/ ctx[0].meta; } const addmetadatabutton = new AddMetadataButton({ props: addmetadatabutton_props }); binding_callbacks.push(() => bind(addmetadatabutton, "meta", addmetadatabutton_meta_binding)); function entrymetadata_meta_binding(value_2) { /*entrymetadata_meta_binding*/ ctx[5].call(null, value_2); } let entrymetadata_props = {}; if (/*entry*/ ctx[0].meta !== void 0) { entrymetadata_props.meta = /*entry*/ ctx[0].meta; } const entrymetadata = new EntryMetadata({ props: entrymetadata_props }); binding_callbacks.push(() => bind(entrymetadata, "meta", entrymetadata_meta_binding)); return { c() { div2 = element("div"); div0 = element("div"); input = element("input"); t0 = space(); h4 = element("h4"); h4.textContent = `${_("Note")}`; t2 = space(); create_component(accountinput.$$.fragment); t3 = space(); create_component(addmetadatabutton.$$.fragment); t4 = space(); div1 = element("div"); textarea = element("textarea"); t5 = space(); create_component(entrymetadata.$$.fragment); attr(input, "type", "date"); attr(input, "name", "date"); input.required = true; attr(div0, "class", "fieldset"); attr(textarea, "name", "comment"); attr(textarea, "rows", "2"); attr(div1, "class", "fieldset"); attr(div2, "class", "entry-form"); }, m(target, anchor) { insert(target, div2, anchor); append(div2, div0); append(div0, input); set_input_value(input, /*entry*/ ctx[0].date); append(div0, t0); append(div0, h4); append(div0, t2); mount_component(accountinput, div0, null); append(div0, t3); mount_component(addmetadatabutton, div0, null); append(div2, t4); append(div2, div1); append(div1, textarea); set_input_value(textarea, /*entry*/ ctx[0].comment); append(div2, t5); mount_component(entrymetadata, div2, null); current = true; dispose = [ listen(input, "input", /*input_input_handler*/ ctx[1]), listen(textarea, "input", /*textarea_input_handler*/ ctx[4]) ]; }, p(ctx, [dirty]) { if (dirty & /*entry*/ 1) { set_input_value(input, /*entry*/ ctx[0].date); } const accountinput_changes = {}; if (!updating_value && dirty & /*entry*/ 1) { updating_value = true; accountinput_changes.value = /*entry*/ ctx[0].account; add_flush_callback(() => updating_value = false); } accountinput.$set(accountinput_changes); const addmetadatabutton_changes = {}; if (!updating_meta && dirty & /*entry*/ 1) { updating_meta = true; addmetadatabutton_changes.meta = /*entry*/ ctx[0].meta; add_flush_callback(() => updating_meta = false); } addmetadatabutton.$set(addmetadatabutton_changes); if (dirty & /*entry*/ 1) { set_input_value(textarea, /*entry*/ ctx[0].comment); } const entrymetadata_changes = {}; if (!updating_meta_1 && dirty & /*entry*/ 1) { updating_meta_1 = true; entrymetadata_changes.meta = /*entry*/ ctx[0].meta; add_flush_callback(() => updating_meta_1 = false); } entrymetadata.$set(entrymetadata_changes); }, i(local) { if (current) return; transition_in(accountinput.$$.fragment, local); transition_in(addmetadatabutton.$$.fragment, local); transition_in(entrymetadata.$$.fragment, local); current = true; }, o(local) { transition_out(accountinput.$$.fragment, local); transition_out(addmetadatabutton.$$.fragment, local); transition_out(entrymetadata.$$.fragment, local); current = false; }, d(detaching) { if (detaching) detach(div2); destroy_component(accountinput); destroy_component(addmetadatabutton); destroy_component(entrymetadata); run_all(dispose); } }; } function instance$k($$self, $$props, $$invalidate) { let { entry } = $$props; function input_input_handler() { entry.date = this.value; $$invalidate(0, entry); } function accountinput_value_binding(value) { entry.account = value; $$invalidate(0, entry); } function addmetadatabutton_meta_binding(value_1) { entry.meta = value_1; $$invalidate(0, entry); } function textarea_input_handler() { entry.comment = this.value; $$invalidate(0, entry); } function entrymetadata_meta_binding(value_2) { entry.meta = value_2; $$invalidate(0, entry); } $$self.$set = $$props => { if ("entry" in $$props) $$invalidate(0, entry = $$props.entry); }; return [ entry, input_input_handler, accountinput_value_binding, addmetadatabutton_meta_binding, textarea_input_handler, entrymetadata_meta_binding ]; } class Note extends SvelteComponent { constructor(options) { super(); init(this, options, instance$k, create_fragment$k, safe_not_equal, { entry: 0 }); } } /* javascript/modals/Extract.svelte generated by Svelte v3.18.2 */ function create_if_block$5(ctx) { let div0; let h3; let t0; let t1_value = /*currentIndex*/ ctx[2] + 1 + ""; let t1; let t2; let t3_value = /*entries*/ ctx[0].length + ""; let t3; let t4; let t5_value = /*entries*/ ctx[0].length - /*duplicates*/ ctx[4] + ""; let t5; let t6; let t7; let span0; let t8; let label; let input; let t9; let t10; let div1; let updating_entry; let t11; let div2; let t12; let span1; let t13; let t14; let hr; let t15; let if_block2_anchor; let current; let dispose; function switch_instance_entry_binding(value) { /*switch_instance_entry_binding*/ ctx[11].call(null, value); } var switch_value = /*component*/ ctx[1]; function switch_props(ctx) { let switch_instance_props = {}; if (/*entry*/ ctx[5] !== void 0) { switch_instance_props.entry = /*entry*/ ctx[5]; } return { props: switch_instance_props }; } if (switch_value) { var switch_instance = new switch_value(switch_props(ctx)); binding_callbacks.push(() => bind(switch_instance, "entry", switch_instance_entry_binding)); } let if_block0 = /*currentIndex*/ ctx[2] > 0 && create_if_block_4(ctx); function select_block_type(ctx, dirty) { if (/*currentIndex*/ ctx[2] < /*entries*/ ctx[0].length - 1) return create_if_block_3; return create_else_block$2; } let current_block_type = select_block_type(ctx); let if_block1 = current_block_type(ctx); let if_block2 = /*entry*/ ctx[5].meta.__source__ && create_if_block_1$2(ctx); return { c() { div0 = element("div"); h3 = element("h3"); t0 = text("Entry "); t1 = text(t1_value); t2 = text(" of "); t3 = text(t3_value); t4 = text(" ("); t5 = text(t5_value); t6 = text("\n to import):"); t7 = space(); span0 = element("span"); t8 = space(); label = element("label"); input = element("input"); t9 = text("\n ignore duplicate"); t10 = space(); div1 = element("div"); if (switch_instance) create_component(switch_instance.$$.fragment); t11 = space(); div2 = element("div"); if (if_block0) if_block0.c(); t12 = space(); span1 = element("span"); t13 = space(); if_block1.c(); t14 = space(); hr = element("hr"); t15 = space(); if (if_block2) if_block2.c(); if_block2_anchor = empty(); attr(span0, "class", "spacer"); attr(input, "type", "checkbox"); input.checked = /*duplicate*/ ctx[3]; attr(label, "class", "button muted"); attr(div0, "class", "headerline"); attr(div1, "class", "ingest-row"); toggle_class(div1, "duplicate", /*duplicate*/ ctx[3]); attr(span1, "class", "spacer"); attr(div2, "class", "fieldset"); }, m(target, anchor) { insert(target, div0, anchor); append(div0, h3); append(h3, t0); append(h3, t1); append(h3, t2); append(h3, t3); append(h3, t4); append(h3, t5); append(h3, t6); append(div0, t7); append(div0, span0); append(div0, t8); append(div0, label); append(label, input); append(label, t9); insert(target, t10, anchor); insert(target, div1, anchor); if (switch_instance) { mount_component(switch_instance, div1, null); } insert(target, t11, anchor); insert(target, div2, anchor); if (if_block0) if_block0.m(div2, null); append(div2, t12); append(div2, span1); append(div2, t13); if_block1.m(div2, null); insert(target, t14, anchor); insert(target, hr, anchor); insert(target, t15, anchor); if (if_block2) if_block2.m(target, anchor); insert(target, if_block2_anchor, anchor); current = true; dispose = listen(input, "click", /*toggleDuplicate*/ ctx[9]); }, p(ctx, dirty) { if ((!current || dirty & /*currentIndex*/ 4) && t1_value !== (t1_value = /*currentIndex*/ ctx[2] + 1 + "")) set_data(t1, t1_value); if ((!current || dirty & /*entries*/ 1) && t3_value !== (t3_value = /*entries*/ ctx[0].length + "")) set_data(t3, t3_value); if ((!current || dirty & /*entries, duplicates*/ 17) && t5_value !== (t5_value = /*entries*/ ctx[0].length - /*duplicates*/ ctx[4] + "")) set_data(t5, t5_value); if (!current || dirty & /*duplicate*/ 8) { input.checked = /*duplicate*/ ctx[3]; } const switch_instance_changes = {}; if (!updating_entry && dirty & /*entry*/ 32) { updating_entry = true; switch_instance_changes.entry = /*entry*/ ctx[5]; add_flush_callback(() => updating_entry = false); } if (switch_value !== (switch_value = /*component*/ ctx[1])) { if (switch_instance) { group_outros(); const old_component = switch_instance; transition_out(old_component.$$.fragment, 1, 0, () => { destroy_component(old_component, 1); }); check_outros(); } if (switch_value) { switch_instance = new switch_value(switch_props(ctx)); binding_callbacks.push(() => bind(switch_instance, "entry", switch_instance_entry_binding)); create_component(switch_instance.$$.fragment); transition_in(switch_instance.$$.fragment, 1); mount_component(switch_instance, div1, null); } else { switch_instance = null; } } else if (switch_value) { switch_instance.$set(switch_instance_changes); } if (dirty & /*duplicate*/ 8) { toggle_class(div1, "duplicate", /*duplicate*/ ctx[3]); } if (/*currentIndex*/ ctx[2] > 0) { if (if_block0) { if_block0.p(ctx, dirty); } else { if_block0 = create_if_block_4(ctx); if_block0.c(); if_block0.m(div2, t12); } } else if (if_block0) { if_block0.d(1); if_block0 = null; } if (current_block_type === (current_block_type = select_block_type(ctx)) && if_block1) { if_block1.p(ctx, dirty); } else { if_block1.d(1); if_block1 = current_block_type(ctx); if (if_block1) { if_block1.c(); if_block1.m(div2, null); } } if (/*entry*/ ctx[5].meta.__source__) { if (if_block2) { if_block2.p(ctx, dirty); } else { if_block2 = create_if_block_1$2(ctx); if_block2.c(); if_block2.m(if_block2_anchor.parentNode, if_block2_anchor); } } else if (if_block2) { if_block2.d(1); if_block2 = null; } }, i(local) { if (current) return; if (switch_instance) transition_in(switch_instance.$$.fragment, local); current = true; }, o(local) { if (switch_instance) transition_out(switch_instance.$$.fragment, local); current = false; }, d(detaching) { if (detaching) detach(div0); if (detaching) detach(t10); if (detaching) detach(div1); if (switch_instance) destroy_component(switch_instance); if (detaching) detach(t11); if (detaching) detach(div2); if (if_block0) if_block0.d(); if_block1.d(); if (detaching) detach(t14); if (detaching) detach(hr); if (detaching) detach(t15); if (if_block2) if_block2.d(detaching); if (detaching) detach(if_block2_anchor); dispose(); } }; } // (79:8) {#if currentIndex > 0} function create_if_block_4(ctx) { let button0; let t1; let button1; let dispose; return { c() { button0 = element("button"); button0.textContent = "⏮"; t1 = space(); button1 = element("button"); button1.textContent = `${_("Previous")}`; attr(button0, "type", "button"); attr(button0, "class", "muted"); attr(button1, "type", "button"); attr(button1, "class", "muted"); }, m(target, anchor) { insert(target, button0, anchor); insert(target, t1, anchor); insert(target, button1, anchor); dispose = [ listen(button0, "click", /*click_handler*/ ctx[12]), listen(button1, "click", /*previousEntry*/ ctx[8]) ]; }, p: noop, d(detaching) { if (detaching) detach(button0); if (detaching) detach(t1); if (detaching) detach(button1); run_all(dispose); } }; } // (103:8) {:else} function create_else_block$2(ctx) { let button; return { c() { button = element("button"); button.textContent = `${_("Save")}`; attr(button, "type", "submit"); }, m(target, anchor) { insert(target, button, anchor); }, p: noop, d(detaching) { if (detaching) detach(button); } }; } // (93:8) {#if currentIndex < entries.length - 1} function create_if_block_3(ctx) { let button0; let t1; let button1; let dispose; return { c() { button0 = element("button"); button0.textContent = `${_("Next")}`; t1 = space(); button1 = element("button"); button1.textContent = "⏭"; attr(button0, "type", "submit"); attr(button1, "type", "button"); attr(button1, "class", "muted"); }, m(target, anchor) { insert(target, button0, anchor); insert(target, t1, anchor); insert(target, button1, anchor); dispose = listen(button1, "click", /*click_handler_1*/ ctx[13]); }, p: noop, d(detaching) { if (detaching) detach(button0); if (detaching) detach(t1); if (detaching) detach(button1); dispose(); } }; } // (108:6) {#if entry.meta.__source__} function create_if_block_1$2(ctx) { let h3; let t0_value = _("Source") + ""; let t0; let t1; let t2; let pre; let t3_value = /*entry*/ ctx[5].meta.__source__ + ""; let t3; let if_block = /*entry*/ ctx[5].meta.lineno > 0 && create_if_block_2$1(ctx); return { c() { h3 = element("h3"); t0 = text(t0_value); t1 = space(); if (if_block) if_block.c(); t2 = space(); pre = element("pre"); t3 = text(t3_value); }, m(target, anchor) { insert(target, h3, anchor); append(h3, t0); append(h3, t1); if (if_block) if_block.m(h3, null); insert(target, t2, anchor); insert(target, pre, anchor); append(pre, t3); }, p(ctx, dirty) { if (/*entry*/ ctx[5].meta.lineno > 0) { if (if_block) { if_block.p(ctx, dirty); } else { if_block = create_if_block_2$1(ctx); if_block.c(); if_block.m(h3, null); } } else if (if_block) { if_block.d(1); if_block = null; } if (dirty & /*entry*/ 32 && t3_value !== (t3_value = /*entry*/ ctx[5].meta.__source__ + "")) set_data(t3, t3_value); }, d(detaching) { if (detaching) detach(h3); if (if_block) if_block.d(); if (detaching) detach(t2); if (detaching) detach(pre); } }; } // (111:10) {#if entry.meta.lineno > 0} function create_if_block_2$1(ctx) { let t0; let t1_value = _("Line") + ""; let t1; let t2; let t3_value = /*entry*/ ctx[5].meta.lineno + ""; let t3; let t4; return { c() { t0 = text("("); t1 = text(t1_value); t2 = text(": "); t3 = text(t3_value); t4 = text(")"); }, m(target, anchor) { insert(target, t0, anchor); insert(target, t1, anchor); insert(target, t2, anchor); insert(target, t3, anchor); insert(target, t4, anchor); }, p(ctx, dirty) { if (dirty & /*entry*/ 32 && t3_value !== (t3_value = /*entry*/ ctx[5].meta.lineno + "")) set_data(t3, t3_value); }, d(detaching) { if (detaching) detach(t0); if (detaching) detach(t1); if (detaching) detach(t2); if (detaching) detach(t3); if (detaching) detach(t4); } }; } // (57:0) function create_default_slot$6(ctx) { let form; let h3; let t1; let current; let dispose; let if_block = /*entry*/ ctx[5] && create_if_block$5(ctx); return { c() { form = element("form"); h3 = element("h3"); h3.textContent = `${_("Import")}`; t1 = space(); if (if_block) if_block.c(); form.noValidate = /*duplicate*/ ctx[3]; }, m(target, anchor) { insert(target, form, anchor); append(form, h3); append(form, t1); if (if_block) if_block.m(form, null); current = true; dispose = listen(form, "submit", prevent_default(/*submitOrNext*/ ctx[7])); }, p(ctx, dirty) { if (/*entry*/ ctx[5]) { if (if_block) { if_block.p(ctx, dirty); transition_in(if_block, 1); } else { if_block = create_if_block$5(ctx); if_block.c(); transition_in(if_block, 1); if_block.m(form, null); } } else if (if_block) { group_outros(); transition_out(if_block, 1, 1, () => { if_block = null; }); check_outros(); } if (!current || dirty & /*duplicate*/ 8) { form.noValidate = /*duplicate*/ ctx[3]; } }, i(local) { if (current) return; transition_in(if_block); current = true; }, o(local) { transition_out(if_block); current = false; }, d(detaching) { if (detaching) detach(form); if (if_block) if_block.d(); dispose(); } }; } function create_fragment$l(ctx) { let current; const modalbase = new ModalBase({ props: { shown: /*shown*/ ctx[6], $$slots: { default: [create_default_slot$6] }, $$scope: { ctx } } }); return { c() { create_component(modalbase.$$.fragment); }, m(target, anchor) { mount_component(modalbase, target, anchor); current = true; }, p(ctx, [dirty]) { const modalbase_changes = {}; if (dirty & /*shown*/ 64) modalbase_changes.shown = /*shown*/ ctx[6]; if (dirty & /*$$scope, duplicate, entry, currentIndex, entries, component, duplicates*/ 16447) { modalbase_changes.$$scope = { dirty, ctx }; } modalbase.$set(modalbase_changes); }, i(local) { if (current) return; transition_in(modalbase.$$.fragment, local); current = true; }, o(local) { transition_out(modalbase.$$.fragment, local); current = false; }, d(detaching) { destroy_component(modalbase, detaching); } }; } function isDuplicate(e) { return !!e.meta.__duplicate__; } function instance$l($$self, $$props, $$invalidate) { let $urlHash; component_subscribe($$self, urlHash, $$value => $$invalidate(10, $urlHash = $$value)); let entries = []; let component; let currentIndex = 0; let duplicate; let duplicates; let entry; let shown; async function submitOrNext() { if (currentIndex < entries.length - 1) { $$invalidate(2, currentIndex += 1); } else { await saveEntries(entries.filter(e => !isDuplicate(e))); closeOverlay(); } } function previousEntry() { $$invalidate(2, currentIndex = Math.max(currentIndex - 1, 0)); } function toggleDuplicate() { $$invalidate(5, entry.meta.__duplicate__ = !entry.meta.__duplicate__, entry); } function switch_instance_entry_binding(value) { entry = value; (((($$invalidate(5, entry), $$invalidate(0, entries)), $$invalidate(2, currentIndex)), $$invalidate(6, shown)), $$invalidate(10, $urlHash)); } const click_handler = () => { $$invalidate(2, currentIndex = 0); }; const click_handler_1 = () => { $$invalidate(2, currentIndex = entries.length - 1); }; $$self.$$.update = () => { if ($$self.$$.dirty & /*$urlHash*/ 1024) { $$invalidate(6, shown = $urlHash.startsWith("extract")); } if ($$self.$$.dirty & /*shown, $urlHash*/ 1088) { if (shown) { const params = new URLSearchParams($urlHash.slice(8)); const filename = params.get("filename"); const importer = params.get("importer"); fetchAPI("extract", { filename, importer }).then(data => { $$invalidate(0, entries = data); }); } } if ($$self.$$.dirty & /*entries, currentIndex*/ 5) { $$invalidate(5, entry = entries[currentIndex]); } if ($$self.$$.dirty & /*entry, entries*/ 33) { if (entry) { $$invalidate(1, component = ({ Balance: Balance$1, Note, Transaction: Transaction_1 })[entry.type]); $$invalidate(4, duplicates = entry && entries.filter(e => isDuplicate(e)).length); $$invalidate(3, duplicate = isDuplicate(entry)); } } }; return [ entries, component, currentIndex, duplicate, duplicates, entry, shown, submitOrNext, previousEntry, toggleDuplicate, $urlHash, switch_instance_entry_binding, click_handler, click_handler_1 ]; } class Extract extends SvelteComponent { constructor(options) { super(); init(this, options, instance$l, create_fragment$l, safe_not_equal, {}); } } /* javascript/modals/Modals.svelte generated by Svelte v3.18.2 */ function create_fragment$m(ctx) { let t0; let t1; let t2; let t3; let current; const addentry = new AddEntry({}); const context = new Context({}); const documentupload = new DocumentUpload({}); const export_1 = new Export({}); const extract = new Extract({}); return { c() { create_component(addentry.$$.fragment); t0 = space(); create_component(context.$$.fragment); t1 = space(); create_component(documentupload.$$.fragment); t2 = space(); create_component(export_1.$$.fragment); t3 = space(); create_component(extract.$$.fragment); }, m(target, anchor) { mount_component(addentry, target, anchor); insert(target, t0, anchor); mount_component(context, target, anchor); insert(target, t1, anchor); mount_component(documentupload, target, anchor); insert(target, t2, anchor); mount_component(export_1, target, anchor); insert(target, t3, anchor); mount_component(extract, target, anchor); current = true; }, p: noop, i(local) { if (current) return; transition_in(addentry.$$.fragment, local); transition_in(context.$$.fragment, local); transition_in(documentupload.$$.fragment, local); transition_in(export_1.$$.fragment, local); transition_in(extract.$$.fragment, local); current = true; }, o(local) { transition_out(addentry.$$.fragment, local); transition_out(context.$$.fragment, local); transition_out(documentupload.$$.fragment, local); transition_out(export_1.$$.fragment, local); transition_out(extract.$$.fragment, local); current = false; }, d(detaching) { destroy_component(addentry, detaching); if (detaching) detach(t0); destroy_component(context, detaching); if (detaching) detach(t1); destroy_component(documentupload, detaching); if (detaching) detach(t2); destroy_component(export_1, detaching); if (detaching) detach(t3); destroy_component(extract, detaching); } }; } class Modals extends SvelteComponent { constructor(options) { super(); init(this, options, null, create_fragment$m, safe_not_equal, {}); } } const stored_history_string = localStorage.getItem("fava-query-history"); let initialList = []; if (stored_history_string) { initialList = JSON.parse(stored_history_string); } const query_shell_history = writable(initialList); query_shell_history.subscribe(val => { if (val.length) { localStorage.setItem("fava-query-history", JSON.stringify(val)); } }); function addToHistory(query) { if (query) { query_shell_history.update(hist => { hist.unshift(query); return [...new Set(hist)]; }); } } /* javascript/query/QueryEditor.svelte generated by Svelte v3.18.2 */ function create_fragment$n(ctx) { let form_1; let button; let dispose; return { c() { form_1 = element("form"); button = element("button"); button.textContent = `${_("Submit")}`; attr(button, "type", "submit"); attr(button, "data-key", "Ctrl/Cmd+Enter"); attr(form_1, "class", "query-box"); attr(form_1, "method", "GET"); }, m(target, anchor) { insert(target, form_1, anchor); append(form_1, button); /*form_1_binding*/ ctx[5](form_1); dispose = listen(form_1, "submit", prevent_default(/*submit_handler*/ ctx[4])); }, p: noop, i: noop, o: noop, d(detaching) { if (detaching) detach(form_1); /*form_1_binding*/ ctx[5](null); dispose(); } }; } function instance$m($$self, $$props, $$invalidate) { let { value = "" } = $$props; let form; let editor; const dispatch = createEventDispatcher(); onMount(() => { const url = new URL(window.location); $$invalidate(2, value = url.searchParams.get("value") || ""); if (value) { dispatch("submit"); } const queryOptions = { value, mode: "beancount-query", extraKeys: { "Ctrl-Enter": () => dispatch("submit"), "Cmd-Enter": () => dispatch("submit") }, placeholder: _("...enter a BQL query. 'help' to list available commands.") }; $$invalidate(3, editor = codemirror( cm => { form.insertBefore(cm, form.firstChild); }, queryOptions )); editor.on("change", cm => { $$invalidate(2, value = cm.getValue()); }); editor.on("keyup", (cm, event) => { if (!cm.state.completionActive && !ignoreKey(event.key)) { codemirror.commands.autocomplete(cm, undefined, { completeSingle: false }); } }); }); const submit_handler = () => dispatch("submit"); function form_1_binding($$value) { binding_callbacks[$$value ? "unshift" : "push"](() => { $$invalidate(0, form = $$value); }); } $$self.$set = $$props => { if ("value" in $$props) $$invalidate(2, value = $$props.value); }; $$self.$$.update = () => { if ($$self.$$.dirty & /*editor, value*/ 12) { if (editor && value !== editor.getValue()) { editor.setValue(value); } } }; return [form, dispatch, value, editor, submit_handler, form_1_binding]; } class QueryEditor extends SvelteComponent { constructor(options) { super(); init(this, options, instance$m, create_fragment$n, safe_not_equal, { value: 2 }); } } /* javascript/query/QueryLinks.svelte generated by Svelte v3.18.2 */ function create_if_block$6(ctx) { let t0; let a0; let t1; let a0_href_value; let t2; let a1; let t3; let a1_href_value; let t4; let a2; let t5; let a2_href_value; return { c() { t0 = text(",\n "); a0 = element("a"); t1 = text("XLS"); t2 = text("\n ,\n "); a1 = element("a"); t3 = text("XLSX"); t4 = text("\n , or\n "); a2 = element("a"); t5 = text("ODS"); attr(a0, "href", a0_href_value = /*queryUrl*/ ctx[2](/*query*/ ctx[0], "xls")); attr(a0, "data-remote", ""); attr(a1, "href", a1_href_value = /*queryUrl*/ ctx[2](/*query*/ ctx[0], "xlsx")); attr(a1, "data-remote", ""); attr(a2, "href", a2_href_value = /*queryUrl*/ ctx[2](/*query*/ ctx[0], "ods")); attr(a2, "data-remote", ""); }, m(target, anchor) { insert(target, t0, anchor); insert(target, a0, anchor); append(a0, t1); insert(target, t2, anchor); insert(target, a1, anchor); append(a1, t3); insert(target, t4, anchor); insert(target, a2, anchor); append(a2, t5); }, p(ctx, dirty) { if (dirty & /*query*/ 1 && a0_href_value !== (a0_href_value = /*queryUrl*/ ctx[2](/*query*/ ctx[0], "xls"))) { attr(a0, "href", a0_href_value); } if (dirty & /*query*/ 1 && a1_href_value !== (a1_href_value = /*queryUrl*/ ctx[2](/*query*/ ctx[0], "xlsx"))) { attr(a1, "href", a1_href_value); } if (dirty & /*query*/ 1 && a2_href_value !== (a2_href_value = /*queryUrl*/ ctx[2](/*query*/ ctx[0], "ods"))) { attr(a2, "href", a2_href_value); } }, d(detaching) { if (detaching) detach(t0); if (detaching) detach(a0); if (detaching) detach(t2); if (detaching) detach(a1); if (detaching) detach(t4); if (detaching) detach(a2); } }; } function create_fragment$o(ctx) { let span; let t0; let t1_value = _("Download as") + ""; let t1; let t2; let a; let t3; let a_href_value; let t4; let t5; let if_block = /*$favaAPIStore*/ ctx[1].have_excel && create_if_block$6(ctx); return { c() { span = element("span"); t0 = text("("); t1 = text(t1_value); t2 = space(); a = element("a"); t3 = text("CSV"); t4 = space(); if (if_block) if_block.c(); t5 = text("\n )"); attr(a, "href", a_href_value = /*queryUrl*/ ctx[2](/*query*/ ctx[0], "csv")); attr(a, "data-remote", ""); attr(span, "class", "download"); }, m(target, anchor) { insert(target, span, anchor); append(span, t0); append(span, t1); append(span, t2); append(span, a); append(a, t3); append(span, t4); if (if_block) if_block.m(span, null); append(span, t5); }, p(ctx, [dirty]) { if (dirty & /*query*/ 1 && a_href_value !== (a_href_value = /*queryUrl*/ ctx[2](/*query*/ ctx[0], "csv"))) { attr(a, "href", a_href_value); } if (/*$favaAPIStore*/ ctx[1].have_excel) { if (if_block) { if_block.p(ctx, dirty); } else { if_block = create_if_block$6(ctx); if_block.c(); if_block.m(span, t5); } } else if (if_block) { if_block.d(1); if_block = null; } }, i: noop, o: noop, d(detaching) { if (detaching) detach(span); if (if_block) if_block.d(); } }; } function instance$n($$self, $$props, $$invalidate) { let $favaAPIStore; component_subscribe($$self, favaAPIStore, $$value => $$invalidate(1, $favaAPIStore = $$value)); let { query = "" } = $$props; function queryUrl(query_string, format) { return urlFor(`download-query/query_result.${format}`, { query_string: query }); } $$self.$set = $$props => { if ("query" in $$props) $$invalidate(0, query = $$props.query); }; return [query, $favaAPIStore, queryUrl]; } class QueryLinks extends SvelteComponent { constructor(options) { super(); init(this, options, instance$n, create_fragment$o, safe_not_equal, { query: 0 }); } } /* javascript/query/Query.svelte generated by Svelte v3.18.2 */ function get_each_context$c(ctx, list, i) { const child_ctx = ctx.slice(); child_ctx[8] = list[i][0]; child_ctx[9] = list[i][1].result; child_ctx[10] = list[i][1].error; return child_ctx; } // (63:8) {#if result} function create_if_block_3$1(ctx) { let span; let t; let current; const querylinks = new QueryLinks({ props: { query: /*history_item*/ ctx[8] } }); return { c() { span = element("span"); t = space(); create_component(querylinks.$$.fragment); attr(span, "class", "spacer"); }, m(target, anchor) { insert(target, span, anchor); insert(target, t, anchor); mount_component(querylinks, target, anchor); current = true; }, p(ctx, dirty) { const querylinks_changes = {}; if (dirty & /*query_result_array*/ 2) querylinks_changes.query = /*history_item*/ ctx[8]; querylinks.$set(querylinks_changes); }, i(local) { if (current) return; transition_in(querylinks.$$.fragment, local); current = true; }, o(local) { transition_out(querylinks.$$.fragment, local); current = false; }, d(detaching) { if (detaching) detach(span); if (detaching) detach(t); destroy_component(querylinks, detaching); } }; } // (74:24) function create_if_block_2$2(ctx) { let html_tag; let raw_value = /*error*/ ctx[10] + ""; return { c() { html_tag = new HtmlTag(raw_value, null); }, m(target, anchor) { html_tag.m(target, anchor); }, p(ctx, dirty) { if (dirty & /*query_result_array*/ 2 && raw_value !== (raw_value = /*error*/ ctx[10] + "")) html_tag.p(raw_value); }, i: noop, o: noop, d(detaching) { if (detaching) html_tag.d(); } }; } // (69:8) {#if result} function create_if_block$7(ctx) { let t; let html_tag; let raw_value = /*result*/ ctx[9].table + ""; let current; let if_block = /*result*/ ctx[9].chart && create_if_block_1$3(ctx); return { c() { if (if_block) if_block.c(); t = space(); html_tag = new HtmlTag(raw_value, null); }, m(target, anchor) { if (if_block) if_block.m(target, anchor); insert(target, t, anchor); html_tag.m(target, anchor); current = true; }, p(ctx, dirty) { if (/*result*/ ctx[9].chart) { if (if_block) { if_block.p(ctx, dirty); transition_in(if_block, 1); } else { if_block = create_if_block_1$3(ctx); if_block.c(); transition_in(if_block, 1); if_block.m(t.parentNode, t); } } else if (if_block) { group_outros(); transition_out(if_block, 1, 1, () => { if_block = null; }); check_outros(); } if ((!current || dirty & /*query_result_array*/ 2) && raw_value !== (raw_value = /*result*/ ctx[9].table + "")) html_tag.p(raw_value); }, i(local) { if (current) return; transition_in(if_block); current = true; }, o(local) { transition_out(if_block); current = false; }, d(detaching) { if (if_block) if_block.d(detaching); if (detaching) detach(t); if (detaching) html_tag.d(); } }; } // (70:10) {#if result.chart} function create_if_block_1$3(ctx) { let current; const chart = new Chart({ props: { chart: /*result*/ ctx[9].chart } }); return { c() { create_component(chart.$$.fragment); }, m(target, anchor) { mount_component(chart, target, anchor); current = true; }, p(ctx, dirty) { const chart_changes = {}; if (dirty & /*query_result_array*/ 2) chart_changes.chart = /*result*/ ctx[9].chart; chart.$set(chart_changes); }, i(local) { if (current) return; transition_in(chart.$$.fragment, local); current = true; }, o(local) { transition_out(chart.$$.fragment, local); current = false; }, d(detaching) { destroy_component(chart, detaching); } }; } // (57:2) {#each query_result_array as [history_item, { result, error } function create_each_block$c(key_1, ctx) { let details; let summary; let pre; let code; let t0_value = /*history_item*/ ctx[8] + ""; let t0; let t1; let t2; let div; let current_block_type_index; let if_block1; let t3; let current; let dispose; let if_block0 = /*result*/ ctx[9] && create_if_block_3$1(ctx); function click_handler(...args) { return /*click_handler*/ ctx[7](/*history_item*/ ctx[8], ...args); } const if_block_creators = [create_if_block$7, create_if_block_2$2]; const if_blocks = []; function select_block_type(ctx, dirty) { if (/*result*/ ctx[9]) return 0; if (/*error*/ ctx[10]) return 1; return -1; } if (~(current_block_type_index = select_block_type(ctx))) { if_block1 = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx); } return { key: key_1, first: null, c() { details = element("details"); summary = element("summary"); pre = element("pre"); code = element("code"); t0 = text(t0_value); t1 = space(); if (if_block0) if_block0.c(); t2 = space(); div = element("div"); if (if_block1) if_block1.c(); t3 = space(); attr(details, "class", "query-result"); toggle_class(details, "error", /*error*/ ctx[10]); this.first = details; }, m(target, anchor) { insert(target, details, anchor); append(details, summary); append(summary, pre); append(pre, code); append(code, t0); append(summary, t1); if (if_block0) if_block0.m(summary, null); append(details, t2); append(details, div); if (~current_block_type_index) { if_blocks[current_block_type_index].m(div, null); } append(details, t3); current = true; dispose = listen(summary, "click", click_handler); }, p(new_ctx, dirty) { ctx = new_ctx; if ((!current || dirty & /*query_result_array*/ 2) && t0_value !== (t0_value = /*history_item*/ ctx[8] + "")) set_data(t0, t0_value); if (/*result*/ ctx[9]) { if (if_block0) { if_block0.p(ctx, dirty); transition_in(if_block0, 1); } else { if_block0 = create_if_block_3$1(ctx); if_block0.c(); transition_in(if_block0, 1); if_block0.m(summary, null); } } else if (if_block0) { group_outros(); transition_out(if_block0, 1, 1, () => { if_block0 = null; }); check_outros(); } let previous_block_index = current_block_type_index; current_block_type_index = select_block_type(ctx); if (current_block_type_index === previous_block_index) { if (~current_block_type_index) { if_blocks[current_block_type_index].p(ctx, dirty); } } else { if (if_block1) { group_outros(); transition_out(if_blocks[previous_block_index], 1, 1, () => { if_blocks[previous_block_index] = null; }); check_outros(); } if (~current_block_type_index) { if_block1 = if_blocks[current_block_type_index]; if (!if_block1) { if_block1 = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx); if_block1.c(); } transition_in(if_block1, 1); if_block1.m(div, null); } else { if_block1 = null; } } if (dirty & /*query_result_array*/ 2) { toggle_class(details, "error", /*error*/ ctx[10]); } }, i(local) { if (current) return; transition_in(if_block0); transition_in(if_block1); current = true; }, o(local) { transition_out(if_block0); transition_out(if_block1); current = false; }, d(detaching) { if (detaching) detach(details); if (if_block0) if_block0.d(); if (~current_block_type_index) { if_blocks[current_block_type_index].d(); } dispose(); } }; } function create_fragment$p(ctx) { let updating_value; let t; let div; let each_blocks = []; let each_1_lookup = new Map(); let current; function queryeditor_value_binding(value) { /*queryeditor_value_binding*/ ctx[6].call(null, value); } let queryeditor_props = {}; if (/*query_string*/ ctx[0] !== void 0) { queryeditor_props.value = /*query_string*/ ctx[0]; } const queryeditor = new QueryEditor({ props: queryeditor_props }); binding_callbacks.push(() => bind(queryeditor, "value", queryeditor_value_binding)); queryeditor.$on("submit", /*submit*/ ctx[2]); let each_value = /*query_result_array*/ ctx[1]; const get_key = ctx => /*history_item*/ ctx[8]; for (let i = 0; i < each_value.length; i += 1) { let child_ctx = get_each_context$c(ctx, each_value, i); let key = get_key(child_ctx); each_1_lookup.set(key, each_blocks[i] = create_each_block$c(key, child_ctx)); } return { c() { create_component(queryeditor.$$.fragment); t = space(); div = element("div"); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].c(); } }, m(target, anchor) { mount_component(queryeditor, target, anchor); insert(target, t, anchor); insert(target, div, anchor); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].m(div, null); } current = true; }, p(ctx, [dirty]) { const queryeditor_changes = {}; if (!updating_value && dirty & /*query_string*/ 1) { updating_value = true; queryeditor_changes.value = /*query_string*/ ctx[0]; add_flush_callback(() => updating_value = false); } queryeditor.$set(queryeditor_changes); const each_value = /*query_result_array*/ ctx[1]; group_outros(); each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx, each_value, each_1_lookup, div, outro_and_destroy_block, create_each_block$c, null, get_each_context$c); check_outros(); }, i(local) { if (current) return; transition_in(queryeditor.$$.fragment, local); for (let i = 0; i < each_value.length; i += 1) { transition_in(each_blocks[i]); } current = true; }, o(local) { transition_out(queryeditor.$$.fragment, local); for (let i = 0; i < each_blocks.length; i += 1) { transition_out(each_blocks[i]); } current = false; }, d(detaching) { destroy_component(queryeditor, detaching); if (detaching) detach(t); if (detaching) detach(div); for (let i = 0; i < each_blocks.length; i += 1) { each_blocks[i].d(); } } }; } function instance$o($$self, $$props, $$invalidate) { let $query_shell_history; component_subscribe($$self, query_shell_history, $$value => $$invalidate(5, $query_shell_history = $$value)); let query_string = ""; const query_results = {}; function submit() { const query = query_string; fetchAPI("query_result", { query_string: query }).then( result => { addToHistory(query); result.chart = parseQueryChart(result.chart); $$invalidate(4, query_results[query] = { result }, query_results); const url = new URL(window.location.toString()); url.searchParams.set("query_string", query); window.history.replaceState(null, "", url.toString()); }, error => { addToHistory(query); // TODO: initSort(); $$invalidate(4, query_results[query] = { error }, query_results); } ); } function click(query) { if (!query_results[query]) { $$invalidate(0, query_string = query); submit(); } } onMount(() => { const url = new URL(window.location); $$invalidate(0, query_string = url.searchParams.get("query_string") || ""); if (query_string) { submit(); } }); function queryeditor_value_binding(value) { query_string = value; $$invalidate(0, query_string); } const click_handler = history_item => click(history_item); let query_result_array; $$self.$$.update = () => { if ($$self.$$.dirty & /*$query_shell_history, query_results*/ 48) { $$invalidate(1, query_result_array = $query_shell_history.map(item => { return [item, query_results[item] || {}]; })); } }; return [ query_string, query_result_array, submit, click, query_results, $query_shell_history, queryeditor_value_binding, click_handler ]; } class Query extends SvelteComponent { constructor(options) { super(); init(this, options, instance$o, create_fragment$p, safe_not_equal, {}); } } /** * Fava's main Javascript entry point. * * The code for Fava's UI is split into several modules that are all imported * below. The different modules can listen to and register events to * communicate and to register DOM event handlers for example. * * The events currently in use in Fava: * * file-modified: * Fetch and update the error count in the sidebar. * * page-init: * Run once the page is initialized, i.e., when the DOM is ready. Use this * for JS code and parts of the UI that are independent of the current * contents of
. * * page-loaded: * After a new page has been loaded asynchronously. Use this to bind to * elements in the page. */ /** * Try to select the given element, load JSON and init Svelte component. * * On the next page load, the component will be removed. */ function initSvelteComponent(selector, SvelteComponent) { const el = select(selector); if (el) { let data = {}; const script = select("script", el); if (script && script.type === "application/json") { data = JSON.parse(script.innerHTML); } const component = new SvelteComponent({ target: el, props: { data } }); e.once("page-loaded", () => component.$destroy()); } } e.on("page-loaded", () => { favaAPIStore.set(favaAPIValidator(getScriptTagJSON("#ledger-data"))); initSvelteComponent("#svelte-charts", ChartSwitcher); initSvelteComponent("#svelte-documents", Documents); initSvelteComponent("#svelte-import", Import); initSvelteComponent("#svelte-query", Query); document.title = favaAPI.documentTitle; select("h1 strong").innerHTML = favaAPI.pageTitle; select("#reload-page").classList.add("hidden"); }); e.on("page-init", () => { // eslint-disable-next-line no-new new Modals({ target: document.body }); const header = select("header"); if (header) { // eslint-disable-next-line no-new new FilterForm({ target: header }); } }); // Check the `changed` API endpoint every 5 seconds and fire the appropriate // events if some file changed. async function doPoll() { try { const changed = await fetchAPI("changed"); if (changed) { if (favaAPI.favaOptions["auto-reload"]) { router.reload(); } else { select("#reload-page").classList.remove("hidden"); e.trigger("file-modified"); notify(_("File change detected. Click to reload."), "warning", () => { router.reload(); }); } } } finally { setTimeout(doPoll, 5000); } } ready().then(() => { favaAPIStore.set(favaAPIValidator(getScriptTagJSON("#ledger-data"))); router.init(); e.trigger("page-init"); e.trigger("page-loaded"); setTimeout(doPoll, 5000); }); }()); //# sourceMappingURL=app.js.map ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1581856590.0 fava-1.14/fava/static/gen/app.js.map0000644000175000001440000733453700000000000017517 0ustar00jakobusers00000000000000{"version":3,"file":"app.js","sources":["../javascript/validation.ts","../node_modules/svelte/internal/index.mjs","../node_modules/svelte/store/index.mjs","../javascript/stores/index.ts","../javascript/helpers.ts","../javascript/events.ts","../javascript/notifications.ts","../javascript/stores/chart.ts","../javascript/router.ts","../node_modules/d3-array/src/ascending.js","../node_modules/d3-array/src/bisector.js","../node_modules/d3-array/src/bisect.js","../node_modules/d3-array/src/extent.js","../node_modules/d3-array/src/identity.js","../node_modules/d3-array/src/group.js","../node_modules/d3-array/src/range.js","../node_modules/d3-array/src/ticks.js","../node_modules/d3-array/src/max.js","../node_modules/d3-array/src/min.js","../node_modules/d3-array/src/merge.js","../node_modules/d3-hierarchy/src/hierarchy/count.js","../node_modules/d3-hierarchy/src/hierarchy/each.js","../node_modules/d3-hierarchy/src/hierarchy/eachBefore.js","../node_modules/d3-hierarchy/src/hierarchy/eachAfter.js","../node_modules/d3-hierarchy/src/hierarchy/sum.js","../node_modules/d3-hierarchy/src/hierarchy/sort.js","../node_modules/d3-hierarchy/src/hierarchy/path.js","../node_modules/d3-hierarchy/src/hierarchy/ancestors.js","../node_modules/d3-hierarchy/src/hierarchy/descendants.js","../node_modules/d3-hierarchy/src/hierarchy/leaves.js","../node_modules/d3-hierarchy/src/hierarchy/links.js","../node_modules/d3-hierarchy/src/hierarchy/index.js","../node_modules/d3-hierarchy/src/accessors.js","../node_modules/d3-hierarchy/src/constant.js","../node_modules/d3-hierarchy/src/treemap/round.js","../node_modules/d3-hierarchy/src/treemap/dice.js","../node_modules/d3-hierarchy/src/partition.js","../node_modules/d3-hierarchy/src/treemap/slice.js","../node_modules/d3-hierarchy/src/treemap/squarify.js","../node_modules/d3-hierarchy/src/treemap/index.js","../node_modules/d3-selection/src/namespaces.js","../node_modules/d3-selection/src/namespace.js","../node_modules/d3-selection/src/creator.js","../node_modules/d3-selection/src/selector.js","../node_modules/d3-selection/src/selection/select.js","../node_modules/d3-selection/src/selectorAll.js","../node_modules/d3-selection/src/selection/selectAll.js","../node_modules/d3-selection/src/matcher.js","../node_modules/d3-selection/src/selection/filter.js","../node_modules/d3-selection/src/selection/sparse.js","../node_modules/d3-selection/src/selection/enter.js","../node_modules/d3-selection/src/constant.js","../node_modules/d3-selection/src/selection/data.js","../node_modules/d3-selection/src/selection/exit.js","../node_modules/d3-selection/src/selection/join.js","../node_modules/d3-selection/src/selection/merge.js","../node_modules/d3-selection/src/selection/order.js","../node_modules/d3-selection/src/selection/sort.js","../node_modules/d3-selection/src/selection/call.js","../node_modules/d3-selection/src/selection/nodes.js","../node_modules/d3-selection/src/selection/node.js","../node_modules/d3-selection/src/selection/size.js","../node_modules/d3-selection/src/selection/empty.js","../node_modules/d3-selection/src/selection/each.js","../node_modules/d3-selection/src/selection/attr.js","../node_modules/d3-selection/src/window.js","../node_modules/d3-selection/src/selection/style.js","../node_modules/d3-selection/src/selection/property.js","../node_modules/d3-selection/src/selection/classed.js","../node_modules/d3-selection/src/selection/text.js","../node_modules/d3-selection/src/selection/html.js","../node_modules/d3-selection/src/selection/raise.js","../node_modules/d3-selection/src/selection/lower.js","../node_modules/d3-selection/src/selection/append.js","../node_modules/d3-selection/src/selection/insert.js","../node_modules/d3-selection/src/selection/remove.js","../node_modules/d3-selection/src/selection/clone.js","../node_modules/d3-selection/src/selection/datum.js","../node_modules/d3-selection/src/selection/on.js","../node_modules/d3-selection/src/selection/dispatch.js","../node_modules/d3-selection/src/selection/index.js","../node_modules/d3-selection/src/select.js","../node_modules/d3-selection/src/point.js","../node_modules/d3-dispatch/src/dispatch.js","../node_modules/d3-timer/src/timer.js","../node_modules/d3-timer/src/timeout.js","../node_modules/d3-transition/src/transition/schedule.js","../node_modules/d3-transition/src/interrupt.js","../node_modules/d3-transition/src/selection/interrupt.js","../node_modules/d3-color/src/define.js","../node_modules/d3-color/src/color.js","../node_modules/d3-color/src/math.js","../node_modules/d3-color/src/lab.js","../node_modules/d3-interpolate/src/constant.js","../node_modules/d3-interpolate/src/color.js","../node_modules/d3-interpolate/src/rgb.js","../node_modules/d3-interpolate/src/numberArray.js","../node_modules/d3-interpolate/src/array.js","../node_modules/d3-interpolate/src/date.js","../node_modules/d3-interpolate/src/number.js","../node_modules/d3-interpolate/src/object.js","../node_modules/d3-interpolate/src/string.js","../node_modules/d3-interpolate/src/value.js","../node_modules/d3-interpolate/src/round.js","../node_modules/d3-interpolate/src/transform/decompose.js","../node_modules/d3-interpolate/src/transform/parse.js","../node_modules/d3-interpolate/src/transform/index.js","../node_modules/d3-transition/src/transition/tween.js","../node_modules/d3-transition/src/transition/interpolate.js","../node_modules/d3-transition/src/transition/attr.js","../node_modules/d3-transition/src/transition/attrTween.js","../node_modules/d3-transition/src/transition/delay.js","../node_modules/d3-transition/src/transition/duration.js","../node_modules/d3-transition/src/transition/ease.js","../node_modules/d3-transition/src/transition/filter.js","../node_modules/d3-transition/src/transition/merge.js","../node_modules/d3-transition/src/transition/on.js","../node_modules/d3-transition/src/transition/remove.js","../node_modules/d3-transition/src/transition/select.js","../node_modules/d3-transition/src/transition/selectAll.js","../node_modules/d3-transition/src/transition/selection.js","../node_modules/d3-transition/src/transition/style.js","../node_modules/d3-transition/src/transition/styleTween.js","../node_modules/d3-transition/src/transition/text.js","../node_modules/d3-transition/src/transition/textTween.js","../node_modules/d3-transition/src/transition/transition.js","../node_modules/d3-transition/src/transition/end.js","../node_modules/d3-transition/src/transition/index.js","../node_modules/d3-ease/src/cubic.js","../node_modules/d3-transition/src/selection/transition.js","../node_modules/d3-transition/src/selection/index.js","../node_modules/d3-format/src/formatDecimal.js","../node_modules/d3-format/src/exponent.js","../node_modules/d3-format/src/formatGroup.js","../node_modules/d3-format/src/formatNumerals.js","../node_modules/d3-format/src/formatSpecifier.js","../node_modules/d3-format/src/formatTrim.js","../node_modules/d3-format/src/formatPrefixAuto.js","../node_modules/d3-format/src/formatRounded.js","../node_modules/d3-format/src/formatTypes.js","../node_modules/d3-format/src/identity.js","../node_modules/d3-format/src/locale.js","../node_modules/d3-format/src/defaultLocale.js","../node_modules/d3-format/src/precisionFixed.js","../node_modules/d3-format/src/precisionPrefix.js","../node_modules/d3-format/src/precisionRound.js","../node_modules/d3-time/src/interval.js","../node_modules/d3-time/src/millisecond.js","../node_modules/d3-time/src/duration.js","../node_modules/d3-time/src/second.js","../node_modules/d3-time/src/day.js","../node_modules/d3-time/src/week.js","../node_modules/d3-time/src/year.js","../node_modules/d3-time/src/utcMinute.js","../node_modules/d3-time/src/utcHour.js","../node_modules/d3-time/src/utcDay.js","../node_modules/d3-time/src/utcWeek.js","../node_modules/d3-time/src/utcMonth.js","../node_modules/d3-time/src/utcYear.js","../node_modules/d3-time-format/src/locale.js","../node_modules/d3-time-format/src/defaultLocale.js","../javascript/format.ts","../node_modules/d3-axis/src/array.js","../node_modules/d3-axis/src/identity.js","../node_modules/d3-axis/src/axis.js","../node_modules/d3-scale/src/init.js","../node_modules/d3-scale/src/ordinal.js","../node_modules/d3-scale/src/band.js","../node_modules/d3-scale/src/constant.js","../node_modules/d3-scale/src/number.js","../node_modules/d3-scale/src/continuous.js","../node_modules/d3-scale/src/tickFormat.js","../node_modules/d3-scale/src/linear.js","../node_modules/d3-scale/src/nice.js","../node_modules/d3-scale/src/pow.js","../node_modules/d3-scale/src/time.js","../node_modules/d3-scale/src/utcTime.js","../javascript/charts/base.ts","../javascript/charts/helpers.ts","../javascript/charts/tooltip.ts","../javascript/charts/bar.ts","../node_modules/d3-path/src/path.js","../node_modules/d3-shape/src/constant.js","../node_modules/d3-shape/src/math.js","../node_modules/d3-shape/src/arc.js","../node_modules/d3-shape/src/curve/linear.js","../node_modules/d3-shape/src/point.js","../node_modules/d3-shape/src/line.js","../node_modules/d3-quadtree/src/add.js","../node_modules/d3-quadtree/src/cover.js","../node_modules/d3-quadtree/src/data.js","../node_modules/d3-quadtree/src/extent.js","../node_modules/d3-quadtree/src/quad.js","../node_modules/d3-quadtree/src/find.js","../node_modules/d3-quadtree/src/remove.js","../node_modules/d3-quadtree/src/root.js","../node_modules/d3-quadtree/src/size.js","../node_modules/d3-quadtree/src/visit.js","../node_modules/d3-quadtree/src/visitAfter.js","../node_modules/d3-quadtree/src/x.js","../node_modules/d3-quadtree/src/y.js","../node_modules/d3-quadtree/src/quadtree.js","../javascript/charts/line.ts","../javascript/charts/scatter.ts","../javascript/charts/hierarchy.ts","../javascript/charts/index.ts","../javascript/clipboard.ts","../node_modules/codemirror/lib/codemirror.js","../node_modules/mousetrap/mousetrap.js","../node_modules/codemirror/addon/mode/simple.js","../node_modules/codemirror/addon/dialog/dialog.js","../node_modules/codemirror/addon/search/searchcursor.js","../node_modules/codemirror/addon/search/search.js","../node_modules/codemirror/addon/display/rulers.js","../node_modules/codemirror/addon/edit/trailingspace.js","../node_modules/codemirror/addon/fold/foldcode.js","../node_modules/codemirror/addon/fold/foldgutter.js","../node_modules/codemirror/addon/hint/show-hint.js","../node_modules/codemirror/addon/comment/comment.js","../node_modules/codemirror/addon/display/placeholder.js","../node_modules/codemirror/addon/selection/active-line.js","../javascript/codemirror/fold-beancount.ts","../javascript/codemirror/helpers.ts","../javascript/codemirror/hint-beancount.ts","../javascript/codemirror/mode-beancount.ts","../javascript/codemirror/bql-grammar.ts","../javascript/codemirror/hint-query.ts","../javascript/codemirror/mode-query.ts","../javascript/editor.ts","../javascript/journal.ts","../javascript/keyboard-shortcuts.ts","../javascript/sidebar.ts","../javascript/sort.ts","../javascript/tree-table.ts","../javascript/api.ts","../javascript/AutocompleteInput.svelte","../javascript/entry-forms/AccountInput.svelte","../javascript/Import.svelte","../javascript/charts/ConversionAndInterval.svelte","../javascript/charts/Chart.svelte","../javascript/charts/ChartSwitcher.svelte","../javascript/FilterForm.svelte","../javascript/documents/util.ts","../javascript/modals/ModalBase.svelte","../javascript/documents/stores.ts","../javascript/documents/Accounts.svelte","../javascript/documents/Table.svelte","../javascript/documents/Documents.svelte","../javascript/entries.ts","../javascript/entry-forms/AddMetadataButton.svelte","../javascript/entry-forms/EntryMetadata.svelte","../javascript/entry-forms/Posting.svelte","../javascript/entry-forms/Transaction.svelte","../javascript/entry-forms/Balance.svelte","../javascript/modals/AddEntry.svelte","../javascript/modals/Context.svelte","../javascript/document-upload.ts","../javascript/modals/DocumentUpload.svelte","../javascript/modals/Export.svelte","../javascript/entry-forms/Note.svelte","../javascript/modals/Extract.svelte","../javascript/stores/query.ts","../javascript/query/QueryEditor.svelte","../javascript/query/QueryLinks.svelte","../javascript/query/Query.svelte","../javascript/main.ts"],"sourcesContent":["/**\n * Data validation.\n *\n * These functions allow us to ensure that `unknown` data obtained from, e.g.,\n * an API, is of a specified type.\n */\n\nclass ValidationError extends Error {}\n\n/**\n * A validator.\n *\n * That is, a function that checks an unknown object to be of a specified type\n * or throw an error otherwise.\n */\nexport interface Validator {\n (json: unknown): T;\n}\n\n/**\n * Validate as unknown (noop).\n */\nexport function unknown(json: unknown): unknown {\n return json;\n}\n\n/**\n * Validate a string.\n */\nexport function string(json: unknown): string {\n if (typeof json === \"string\") {\n return json;\n }\n throw new ValidationError(`Expected a string, got '${json}' instead.`);\n}\n\n/**\n * Validate a boolean.\n */\nexport function boolean(json: unknown): boolean {\n if (typeof json === \"boolean\") {\n return json;\n }\n throw new ValidationError(`Expected a boolean, got '${json}' instead.`);\n}\n\n/**\n * Validate a number.\n */\nexport function number(json: unknown): number {\n if (typeof json === \"number\") {\n return json;\n }\n throw new ValidationError(`Expected a number, got '${json}' instead.`);\n}\n\n/**\n * Validate a date (from a string).\n */\nexport function date(json: unknown): Date {\n if (typeof json === \"string\" || json instanceof Date) {\n return new Date(json);\n }\n throw new ValidationError(`Expected a date: ${json}`);\n}\n\n/**\n * Validate a value to be equal to a constant value.\n */\nexport function constant(value: T): Validator {\n return (json: unknown) => {\n if (json === value) {\n return json as T;\n }\n throw new ValidationError(`Expected a constant: ${json}`);\n };\n}\n\n/**\n * Validate a value that is of one of two given types.\n */\nexport function union(\n a: Validator,\n b: Validator\n): Validator {\n return (json: unknown) => {\n for (const validator of [a, b]) {\n try {\n return validator(json);\n } catch (exc) {\n // pass\n }\n }\n throw new ValidationError(`Validating union failed`);\n };\n}\n\n/**\n * Validator for an object that might be undefined.\n */\nexport function optional(validator: Validator): Validator {\n return (json: unknown) => {\n return json === undefined ? undefined : validator(json);\n };\n}\n\n/**\n * Lazy validator to allow for recursive structures.\n */\nexport function lazy(func: () => Validator): Validator {\n return (json: unknown) => {\n return func()(json);\n };\n}\n\n/**\n * Validator for an array of values.\n */\nexport function array(validator: Validator): Validator {\n return (json: unknown) => {\n if (Array.isArray(json)) {\n const result: T[] = [];\n json.forEach(element => {\n result.push(validator(element));\n });\n return result;\n }\n throw new ValidationError(`Expected an array: ${json}`);\n };\n}\n\n/**\n * Validator for a tuple of fixed length.\n */\nexport function tuple(\n decoders: [Validator, Validator]\n): Validator<[A, B]> {\n return (json: unknown) => {\n if (Array.isArray(json) && json.length === 2) {\n const result = [];\n\n for (let i = 0; i < decoders.length; i += 1) {\n result[i] = decoders[i](json[i]);\n }\n return result as [A, B];\n }\n throw new ValidationError(`Expected a tuple: ${json}`);\n };\n}\n\nconst isJsonObject = (json: unknown): json is Record =>\n typeof json === \"object\" && json !== null && !Array.isArray(json);\n\n/**\n * Validator for an object with some given properties.\n */\nexport function object(\n validators: { [t in keyof T]: Validator }\n): Validator {\n return (json: unknown) => {\n if (isJsonObject(json)) {\n const obj: Partial = {};\n // eslint-disable-next-line no-restricted-syntax\n for (const key in validators) {\n if (Object.prototype.hasOwnProperty.call(validators, key)) {\n obj[key] = validators[key](json[key]);\n }\n }\n return obj as T;\n }\n throw new ValidationError();\n };\n}\n\n/**\n * Validator for a dict-like structure.\n */\nexport function record(decoder: Validator): Validator> {\n return (json: unknown) => {\n if (isJsonObject(json)) {\n const ret: Record = {};\n // eslint-disable-next-line no-restricted-syntax\n for (const key in json) {\n if (Object.prototype.hasOwnProperty.call(json, key)) {\n ret[key] = decoder(json[key]);\n }\n }\n return ret;\n }\n throw new ValidationError();\n };\n}\n","function noop() { }\nconst identity = x => x;\nfunction assign(tar, src) {\n // @ts-ignore\n for (const k in src)\n tar[k] = src[k];\n return tar;\n}\nfunction is_promise(value) {\n return value && typeof value === 'object' && typeof value.then === 'function';\n}\nfunction add_location(element, file, line, column, char) {\n element.__svelte_meta = {\n loc: { file, line, column, char }\n };\n}\nfunction run(fn) {\n return fn();\n}\nfunction blank_object() {\n return Object.create(null);\n}\nfunction run_all(fns) {\n fns.forEach(run);\n}\nfunction is_function(thing) {\n return typeof thing === 'function';\n}\nfunction safe_not_equal(a, b) {\n return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');\n}\nfunction not_equal(a, b) {\n return a != a ? b == b : a !== b;\n}\nfunction validate_store(store, name) {\n if (store != null && typeof store.subscribe !== 'function') {\n throw new Error(`'${name}' is not a store with a 'subscribe' method`);\n }\n}\nfunction subscribe(store, ...callbacks) {\n if (store == null) {\n return noop;\n }\n const unsub = store.subscribe(...callbacks);\n return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;\n}\nfunction get_store_value(store) {\n let value;\n subscribe(store, _ => value = _)();\n return value;\n}\nfunction component_subscribe(component, store, callback) {\n component.$$.on_destroy.push(subscribe(store, callback));\n}\nfunction create_slot(definition, ctx, $$scope, fn) {\n if (definition) {\n const slot_ctx = get_slot_context(definition, ctx, $$scope, fn);\n return definition[0](slot_ctx);\n }\n}\nfunction get_slot_context(definition, ctx, $$scope, fn) {\n return definition[1] && fn\n ? assign($$scope.ctx.slice(), definition[1](fn(ctx)))\n : $$scope.ctx;\n}\nfunction get_slot_changes(definition, $$scope, dirty, fn) {\n if (definition[2] && fn) {\n const lets = definition[2](fn(dirty));\n if (typeof $$scope.dirty === 'object') {\n const merged = [];\n const len = Math.max($$scope.dirty.length, lets.length);\n for (let i = 0; i < len; i += 1) {\n merged[i] = $$scope.dirty[i] | lets[i];\n }\n return merged;\n }\n return $$scope.dirty | lets;\n }\n return $$scope.dirty;\n}\nfunction exclude_internal_props(props) {\n const result = {};\n for (const k in props)\n if (k[0] !== '$')\n result[k] = props[k];\n return result;\n}\nfunction once(fn) {\n let ran = false;\n return function (...args) {\n if (ran)\n return;\n ran = true;\n fn.call(this, ...args);\n };\n}\nfunction null_to_empty(value) {\n return value == null ? '' : value;\n}\nfunction set_store_value(store, ret, value = ret) {\n store.set(value);\n return ret;\n}\nconst has_prop = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);\nfunction action_destroyer(action_result) {\n return action_result && is_function(action_result.destroy) ? action_result.destroy : noop;\n}\n\nconst is_client = typeof window !== 'undefined';\nlet now = is_client\n ? () => window.performance.now()\n : () => Date.now();\nlet raf = is_client ? cb => requestAnimationFrame(cb) : noop;\n// used internally for testing\nfunction set_now(fn) {\n now = fn;\n}\nfunction set_raf(fn) {\n raf = fn;\n}\n\nconst tasks = new Set();\nfunction run_tasks(now) {\n tasks.forEach(task => {\n if (!task.c(now)) {\n tasks.delete(task);\n task.f();\n }\n });\n if (tasks.size !== 0)\n raf(run_tasks);\n}\n/**\n * For testing purposes only!\n */\nfunction clear_loops() {\n tasks.clear();\n}\n/**\n * Creates a new task that runs on each raf frame\n * until it returns a falsy value or is aborted\n */\nfunction loop(callback) {\n let task;\n if (tasks.size === 0)\n raf(run_tasks);\n return {\n promise: new Promise(fulfill => {\n tasks.add(task = { c: callback, f: fulfill });\n }),\n abort() {\n tasks.delete(task);\n }\n };\n}\n\nfunction append(target, node) {\n target.appendChild(node);\n}\nfunction insert(target, node, anchor) {\n target.insertBefore(node, anchor || null);\n}\nfunction detach(node) {\n node.parentNode.removeChild(node);\n}\nfunction destroy_each(iterations, detaching) {\n for (let i = 0; i < iterations.length; i += 1) {\n if (iterations[i])\n iterations[i].d(detaching);\n }\n}\nfunction element(name) {\n return document.createElement(name);\n}\nfunction element_is(name, is) {\n return document.createElement(name, { is });\n}\nfunction object_without_properties(obj, exclude) {\n const target = {};\n for (const k in obj) {\n if (has_prop(obj, k)\n // @ts-ignore\n && exclude.indexOf(k) === -1) {\n // @ts-ignore\n target[k] = obj[k];\n }\n }\n return target;\n}\nfunction svg_element(name) {\n return document.createElementNS('http://www.w3.org/2000/svg', name);\n}\nfunction text(data) {\n return document.createTextNode(data);\n}\nfunction space() {\n return text(' ');\n}\nfunction empty() {\n return text('');\n}\nfunction listen(node, event, handler, options) {\n node.addEventListener(event, handler, options);\n return () => node.removeEventListener(event, handler, options);\n}\nfunction prevent_default(fn) {\n return function (event) {\n event.preventDefault();\n // @ts-ignore\n return fn.call(this, event);\n };\n}\nfunction stop_propagation(fn) {\n return function (event) {\n event.stopPropagation();\n // @ts-ignore\n return fn.call(this, event);\n };\n}\nfunction self(fn) {\n return function (event) {\n // @ts-ignore\n if (event.target === this)\n fn.call(this, event);\n };\n}\nfunction attr(node, attribute, value) {\n if (value == null)\n node.removeAttribute(attribute);\n else if (node.getAttribute(attribute) !== value)\n node.setAttribute(attribute, value);\n}\nfunction set_attributes(node, attributes) {\n // @ts-ignore\n const descriptors = Object.getOwnPropertyDescriptors(node.__proto__);\n for (const key in attributes) {\n if (attributes[key] == null) {\n node.removeAttribute(key);\n }\n else if (key === 'style') {\n node.style.cssText = attributes[key];\n }\n else if (descriptors[key] && descriptors[key].set) {\n node[key] = attributes[key];\n }\n else {\n attr(node, key, attributes[key]);\n }\n }\n}\nfunction set_svg_attributes(node, attributes) {\n for (const key in attributes) {\n attr(node, key, attributes[key]);\n }\n}\nfunction set_custom_element_data(node, prop, value) {\n if (prop in node) {\n node[prop] = value;\n }\n else {\n attr(node, prop, value);\n }\n}\nfunction xlink_attr(node, attribute, value) {\n node.setAttributeNS('http://www.w3.org/1999/xlink', attribute, value);\n}\nfunction get_binding_group_value(group) {\n const value = [];\n for (let i = 0; i < group.length; i += 1) {\n if (group[i].checked)\n value.push(group[i].__value);\n }\n return value;\n}\nfunction to_number(value) {\n return value === '' ? undefined : +value;\n}\nfunction time_ranges_to_array(ranges) {\n const array = [];\n for (let i = 0; i < ranges.length; i += 1) {\n array.push({ start: ranges.start(i), end: ranges.end(i) });\n }\n return array;\n}\nfunction children(element) {\n return Array.from(element.childNodes);\n}\nfunction claim_element(nodes, name, attributes, svg) {\n for (let i = 0; i < nodes.length; i += 1) {\n const node = nodes[i];\n if (node.nodeName === name) {\n let j = 0;\n while (j < node.attributes.length) {\n const attribute = node.attributes[j];\n if (attributes[attribute.name]) {\n j++;\n }\n else {\n node.removeAttribute(attribute.name);\n }\n }\n return nodes.splice(i, 1)[0];\n }\n }\n return svg ? svg_element(name) : element(name);\n}\nfunction claim_text(nodes, data) {\n for (let i = 0; i < nodes.length; i += 1) {\n const node = nodes[i];\n if (node.nodeType === 3) {\n node.data = '' + data;\n return nodes.splice(i, 1)[0];\n }\n }\n return text(data);\n}\nfunction claim_space(nodes) {\n return claim_text(nodes, ' ');\n}\nfunction set_data(text, data) {\n data = '' + data;\n if (text.data !== data)\n text.data = data;\n}\nfunction set_input_value(input, value) {\n if (value != null || input.value) {\n input.value = value;\n }\n}\nfunction set_input_type(input, type) {\n try {\n input.type = type;\n }\n catch (e) {\n // do nothing\n }\n}\nfunction set_style(node, key, value, important) {\n node.style.setProperty(key, value, important ? 'important' : '');\n}\nfunction select_option(select, value) {\n for (let i = 0; i < select.options.length; i += 1) {\n const option = select.options[i];\n if (option.__value === value) {\n option.selected = true;\n return;\n }\n }\n}\nfunction select_options(select, value) {\n for (let i = 0; i < select.options.length; i += 1) {\n const option = select.options[i];\n option.selected = ~value.indexOf(option.__value);\n }\n}\nfunction select_value(select) {\n const selected_option = select.querySelector(':checked') || select.options[0];\n return selected_option && selected_option.__value;\n}\nfunction select_multiple_value(select) {\n return [].map.call(select.querySelectorAll(':checked'), option => option.__value);\n}\nfunction add_resize_listener(element, fn) {\n if (getComputedStyle(element).position === 'static') {\n element.style.position = 'relative';\n }\n const object = document.createElement('object');\n object.setAttribute('style', 'display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; pointer-events: none; z-index: -1;');\n object.setAttribute('aria-hidden', 'true');\n object.type = 'text/html';\n object.tabIndex = -1;\n let win;\n object.onload = () => {\n win = object.contentDocument.defaultView;\n win.addEventListener('resize', fn);\n };\n if (/Trident/.test(navigator.userAgent)) {\n element.appendChild(object);\n object.data = 'about:blank';\n }\n else {\n object.data = 'about:blank';\n element.appendChild(object);\n }\n return {\n cancel: () => {\n win && win.removeEventListener && win.removeEventListener('resize', fn);\n element.removeChild(object);\n }\n };\n}\nfunction toggle_class(element, name, toggle) {\n element.classList[toggle ? 'add' : 'remove'](name);\n}\nfunction custom_event(type, detail) {\n const e = document.createEvent('CustomEvent');\n e.initCustomEvent(type, false, false, detail);\n return e;\n}\nfunction query_selector_all(selector, parent = document.body) {\n return Array.from(parent.querySelectorAll(selector));\n}\nclass HtmlTag {\n constructor(html, anchor = null) {\n this.e = element('div');\n this.a = anchor;\n this.u(html);\n }\n m(target, anchor = null) {\n for (let i = 0; i < this.n.length; i += 1) {\n insert(target, this.n[i], anchor);\n }\n this.t = target;\n }\n u(html) {\n this.e.innerHTML = html;\n this.n = Array.from(this.e.childNodes);\n }\n p(html) {\n this.d();\n this.u(html);\n this.m(this.t, this.a);\n }\n d() {\n this.n.forEach(detach);\n }\n}\n\nlet stylesheet;\nlet active = 0;\nlet current_rules = {};\n// https://github.com/darkskyapp/string-hash/blob/master/index.js\nfunction hash(str) {\n let hash = 5381;\n let i = str.length;\n while (i--)\n hash = ((hash << 5) - hash) ^ str.charCodeAt(i);\n return hash >>> 0;\n}\nfunction create_rule(node, a, b, duration, delay, ease, fn, uid = 0) {\n const step = 16.666 / duration;\n let keyframes = '{\\n';\n for (let p = 0; p <= 1; p += step) {\n const t = a + (b - a) * ease(p);\n keyframes += p * 100 + `%{${fn(t, 1 - t)}}\\n`;\n }\n const rule = keyframes + `100% {${fn(b, 1 - b)}}\\n}`;\n const name = `__svelte_${hash(rule)}_${uid}`;\n if (!current_rules[name]) {\n if (!stylesheet) {\n const style = element('style');\n document.head.appendChild(style);\n stylesheet = style.sheet;\n }\n current_rules[name] = true;\n stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length);\n }\n const animation = node.style.animation || '';\n node.style.animation = `${animation ? `${animation}, ` : ``}${name} ${duration}ms linear ${delay}ms 1 both`;\n active += 1;\n return name;\n}\nfunction delete_rule(node, name) {\n node.style.animation = (node.style.animation || '')\n .split(', ')\n .filter(name\n ? anim => anim.indexOf(name) < 0 // remove specific animation\n : anim => anim.indexOf('__svelte') === -1 // remove all Svelte animations\n )\n .join(', ');\n if (name && !--active)\n clear_rules();\n}\nfunction clear_rules() {\n raf(() => {\n if (active)\n return;\n let i = stylesheet.cssRules.length;\n while (i--)\n stylesheet.deleteRule(i);\n current_rules = {};\n });\n}\n\nfunction create_animation(node, from, fn, params) {\n if (!from)\n return noop;\n const to = node.getBoundingClientRect();\n if (from.left === to.left && from.right === to.right && from.top === to.top && from.bottom === to.bottom)\n return noop;\n const { delay = 0, duration = 300, easing = identity, \n // @ts-ignore todo: should this be separated from destructuring? Or start/end added to public api and documentation?\n start: start_time = now() + delay, \n // @ts-ignore todo:\n end = start_time + duration, tick = noop, css } = fn(node, { from, to }, params);\n let running = true;\n let started = false;\n let name;\n function start() {\n if (css) {\n name = create_rule(node, 0, 1, duration, delay, easing, css);\n }\n if (!delay) {\n started = true;\n }\n }\n function stop() {\n if (css)\n delete_rule(node, name);\n running = false;\n }\n loop(now => {\n if (!started && now >= start_time) {\n started = true;\n }\n if (started && now >= end) {\n tick(1, 0);\n stop();\n }\n if (!running) {\n return false;\n }\n if (started) {\n const p = now - start_time;\n const t = 0 + 1 * easing(p / duration);\n tick(t, 1 - t);\n }\n return true;\n });\n start();\n tick(0, 1);\n return stop;\n}\nfunction fix_position(node) {\n const style = getComputedStyle(node);\n if (style.position !== 'absolute' && style.position !== 'fixed') {\n const { width, height } = style;\n const a = node.getBoundingClientRect();\n node.style.position = 'absolute';\n node.style.width = width;\n node.style.height = height;\n add_transform(node, a);\n }\n}\nfunction add_transform(node, a) {\n const b = node.getBoundingClientRect();\n if (a.left !== b.left || a.top !== b.top) {\n const style = getComputedStyle(node);\n const transform = style.transform === 'none' ? '' : style.transform;\n node.style.transform = `${transform} translate(${a.left - b.left}px, ${a.top - b.top}px)`;\n }\n}\n\nlet current_component;\nfunction set_current_component(component) {\n current_component = component;\n}\nfunction get_current_component() {\n if (!current_component)\n throw new Error(`Function called outside component initialization`);\n return current_component;\n}\nfunction beforeUpdate(fn) {\n get_current_component().$$.before_update.push(fn);\n}\nfunction onMount(fn) {\n get_current_component().$$.on_mount.push(fn);\n}\nfunction afterUpdate(fn) {\n get_current_component().$$.after_update.push(fn);\n}\nfunction onDestroy(fn) {\n get_current_component().$$.on_destroy.push(fn);\n}\nfunction createEventDispatcher() {\n const component = get_current_component();\n return (type, detail) => {\n const callbacks = component.$$.callbacks[type];\n if (callbacks) {\n // TODO are there situations where events could be dispatched\n // in a server (non-DOM) environment?\n const event = custom_event(type, detail);\n callbacks.slice().forEach(fn => {\n fn.call(component, event);\n });\n }\n };\n}\nfunction setContext(key, context) {\n get_current_component().$$.context.set(key, context);\n}\nfunction getContext(key) {\n return get_current_component().$$.context.get(key);\n}\n// TODO figure out if we still want to support\n// shorthand events, or if we want to implement\n// a real bubbling mechanism\nfunction bubble(component, event) {\n const callbacks = component.$$.callbacks[event.type];\n if (callbacks) {\n callbacks.slice().forEach(fn => fn(event));\n }\n}\n\nconst dirty_components = [];\nconst intros = { enabled: false };\nconst binding_callbacks = [];\nconst render_callbacks = [];\nconst flush_callbacks = [];\nconst resolved_promise = Promise.resolve();\nlet update_scheduled = false;\nfunction schedule_update() {\n if (!update_scheduled) {\n update_scheduled = true;\n resolved_promise.then(flush);\n }\n}\nfunction tick() {\n schedule_update();\n return resolved_promise;\n}\nfunction add_render_callback(fn) {\n render_callbacks.push(fn);\n}\nfunction add_flush_callback(fn) {\n flush_callbacks.push(fn);\n}\nlet flushing = false;\nconst seen_callbacks = new Set();\nfunction flush() {\n if (flushing)\n return;\n flushing = true;\n do {\n // first, call beforeUpdate functions\n // and update components\n for (let i = 0; i < dirty_components.length; i += 1) {\n const component = dirty_components[i];\n set_current_component(component);\n update(component.$$);\n }\n dirty_components.length = 0;\n while (binding_callbacks.length)\n binding_callbacks.pop()();\n // then, once components are updated, call\n // afterUpdate functions. This may cause\n // subsequent updates...\n for (let i = 0; i < render_callbacks.length; i += 1) {\n const callback = render_callbacks[i];\n if (!seen_callbacks.has(callback)) {\n // ...so guard against infinite loops\n seen_callbacks.add(callback);\n callback();\n }\n }\n render_callbacks.length = 0;\n } while (dirty_components.length);\n while (flush_callbacks.length) {\n flush_callbacks.pop()();\n }\n update_scheduled = false;\n flushing = false;\n seen_callbacks.clear();\n}\nfunction update($$) {\n if ($$.fragment !== null) {\n $$.update();\n run_all($$.before_update);\n const dirty = $$.dirty;\n $$.dirty = [-1];\n $$.fragment && $$.fragment.p($$.ctx, dirty);\n $$.after_update.forEach(add_render_callback);\n }\n}\n\nlet promise;\nfunction wait() {\n if (!promise) {\n promise = Promise.resolve();\n promise.then(() => {\n promise = null;\n });\n }\n return promise;\n}\nfunction dispatch(node, direction, kind) {\n node.dispatchEvent(custom_event(`${direction ? 'intro' : 'outro'}${kind}`));\n}\nconst outroing = new Set();\nlet outros;\nfunction group_outros() {\n outros = {\n r: 0,\n c: [],\n p: outros // parent group\n };\n}\nfunction check_outros() {\n if (!outros.r) {\n run_all(outros.c);\n }\n outros = outros.p;\n}\nfunction transition_in(block, local) {\n if (block && block.i) {\n outroing.delete(block);\n block.i(local);\n }\n}\nfunction transition_out(block, local, detach, callback) {\n if (block && block.o) {\n if (outroing.has(block))\n return;\n outroing.add(block);\n outros.c.push(() => {\n outroing.delete(block);\n if (callback) {\n if (detach)\n block.d(1);\n callback();\n }\n });\n block.o(local);\n }\n}\nconst null_transition = { duration: 0 };\nfunction create_in_transition(node, fn, params) {\n let config = fn(node, params);\n let running = false;\n let animation_name;\n let task;\n let uid = 0;\n function cleanup() {\n if (animation_name)\n delete_rule(node, animation_name);\n }\n function go() {\n const { delay = 0, duration = 300, easing = identity, tick = noop, css } = config || null_transition;\n if (css)\n animation_name = create_rule(node, 0, 1, duration, delay, easing, css, uid++);\n tick(0, 1);\n const start_time = now() + delay;\n const end_time = start_time + duration;\n if (task)\n task.abort();\n running = true;\n add_render_callback(() => dispatch(node, true, 'start'));\n task = loop(now => {\n if (running) {\n if (now >= end_time) {\n tick(1, 0);\n dispatch(node, true, 'end');\n cleanup();\n return running = false;\n }\n if (now >= start_time) {\n const t = easing((now - start_time) / duration);\n tick(t, 1 - t);\n }\n }\n return running;\n });\n }\n let started = false;\n return {\n start() {\n if (started)\n return;\n delete_rule(node);\n if (is_function(config)) {\n config = config();\n wait().then(go);\n }\n else {\n go();\n }\n },\n invalidate() {\n started = false;\n },\n end() {\n if (running) {\n cleanup();\n running = false;\n }\n }\n };\n}\nfunction create_out_transition(node, fn, params) {\n let config = fn(node, params);\n let running = true;\n let animation_name;\n const group = outros;\n group.r += 1;\n function go() {\n const { delay = 0, duration = 300, easing = identity, tick = noop, css } = config || null_transition;\n if (css)\n animation_name = create_rule(node, 1, 0, duration, delay, easing, css);\n const start_time = now() + delay;\n const end_time = start_time + duration;\n add_render_callback(() => dispatch(node, false, 'start'));\n loop(now => {\n if (running) {\n if (now >= end_time) {\n tick(0, 1);\n dispatch(node, false, 'end');\n if (!--group.r) {\n // this will result in `end()` being called,\n // so we don't need to clean up here\n run_all(group.c);\n }\n return false;\n }\n if (now >= start_time) {\n const t = easing((now - start_time) / duration);\n tick(1 - t, t);\n }\n }\n return running;\n });\n }\n if (is_function(config)) {\n wait().then(() => {\n // @ts-ignore\n config = config();\n go();\n });\n }\n else {\n go();\n }\n return {\n end(reset) {\n if (reset && config.tick) {\n config.tick(1, 0);\n }\n if (running) {\n if (animation_name)\n delete_rule(node, animation_name);\n running = false;\n }\n }\n };\n}\nfunction create_bidirectional_transition(node, fn, params, intro) {\n let config = fn(node, params);\n let t = intro ? 0 : 1;\n let running_program = null;\n let pending_program = null;\n let animation_name = null;\n function clear_animation() {\n if (animation_name)\n delete_rule(node, animation_name);\n }\n function init(program, duration) {\n const d = program.b - t;\n duration *= Math.abs(d);\n return {\n a: t,\n b: program.b,\n d,\n duration,\n start: program.start,\n end: program.start + duration,\n group: program.group\n };\n }\n function go(b) {\n const { delay = 0, duration = 300, easing = identity, tick = noop, css } = config || null_transition;\n const program = {\n start: now() + delay,\n b\n };\n if (!b) {\n // @ts-ignore todo: improve typings\n program.group = outros;\n outros.r += 1;\n }\n if (running_program) {\n pending_program = program;\n }\n else {\n // if this is an intro, and there's a delay, we need to do\n // an initial tick and/or apply CSS animation immediately\n if (css) {\n clear_animation();\n animation_name = create_rule(node, t, b, duration, delay, easing, css);\n }\n if (b)\n tick(0, 1);\n running_program = init(program, duration);\n add_render_callback(() => dispatch(node, b, 'start'));\n loop(now => {\n if (pending_program && now > pending_program.start) {\n running_program = init(pending_program, duration);\n pending_program = null;\n dispatch(node, running_program.b, 'start');\n if (css) {\n clear_animation();\n animation_name = create_rule(node, t, running_program.b, running_program.duration, 0, easing, config.css);\n }\n }\n if (running_program) {\n if (now >= running_program.end) {\n tick(t = running_program.b, 1 - t);\n dispatch(node, running_program.b, 'end');\n if (!pending_program) {\n // we're done\n if (running_program.b) {\n // intro — we can tidy up immediately\n clear_animation();\n }\n else {\n // outro — needs to be coordinated\n if (!--running_program.group.r)\n run_all(running_program.group.c);\n }\n }\n running_program = null;\n }\n else if (now >= running_program.start) {\n const p = now - running_program.start;\n t = running_program.a + running_program.d * easing(p / running_program.duration);\n tick(t, 1 - t);\n }\n }\n return !!(running_program || pending_program);\n });\n }\n }\n return {\n run(b) {\n if (is_function(config)) {\n wait().then(() => {\n // @ts-ignore\n config = config();\n go(b);\n });\n }\n else {\n go(b);\n }\n },\n end() {\n clear_animation();\n running_program = pending_program = null;\n }\n };\n}\n\nfunction handle_promise(promise, info) {\n const token = info.token = {};\n function update(type, index, key, value) {\n if (info.token !== token)\n return;\n info.resolved = value;\n let child_ctx = info.ctx;\n if (key !== undefined) {\n child_ctx = child_ctx.slice();\n child_ctx[key] = value;\n }\n const block = type && (info.current = type)(child_ctx);\n let needs_flush = false;\n if (info.block) {\n if (info.blocks) {\n info.blocks.forEach((block, i) => {\n if (i !== index && block) {\n group_outros();\n transition_out(block, 1, 1, () => {\n info.blocks[i] = null;\n });\n check_outros();\n }\n });\n }\n else {\n info.block.d(1);\n }\n block.c();\n transition_in(block, 1);\n block.m(info.mount(), info.anchor);\n needs_flush = true;\n }\n info.block = block;\n if (info.blocks)\n info.blocks[index] = block;\n if (needs_flush) {\n flush();\n }\n }\n if (is_promise(promise)) {\n const current_component = get_current_component();\n promise.then(value => {\n set_current_component(current_component);\n update(info.then, 1, info.value, value);\n set_current_component(null);\n }, error => {\n set_current_component(current_component);\n update(info.catch, 2, info.error, error);\n set_current_component(null);\n });\n // if we previously had a then/catch block, destroy it\n if (info.current !== info.pending) {\n update(info.pending, 0);\n return true;\n }\n }\n else {\n if (info.current !== info.then) {\n update(info.then, 1, info.value, promise);\n return true;\n }\n info.resolved = promise;\n }\n}\n\nconst globals = (typeof window !== 'undefined' ? window : global);\n\nfunction destroy_block(block, lookup) {\n block.d(1);\n lookup.delete(block.key);\n}\nfunction outro_and_destroy_block(block, lookup) {\n transition_out(block, 1, 1, () => {\n lookup.delete(block.key);\n });\n}\nfunction fix_and_destroy_block(block, lookup) {\n block.f();\n destroy_block(block, lookup);\n}\nfunction fix_and_outro_and_destroy_block(block, lookup) {\n block.f();\n outro_and_destroy_block(block, lookup);\n}\nfunction update_keyed_each(old_blocks, dirty, get_key, dynamic, ctx, list, lookup, node, destroy, create_each_block, next, get_context) {\n let o = old_blocks.length;\n let n = list.length;\n let i = o;\n const old_indexes = {};\n while (i--)\n old_indexes[old_blocks[i].key] = i;\n const new_blocks = [];\n const new_lookup = new Map();\n const deltas = new Map();\n i = n;\n while (i--) {\n const child_ctx = get_context(ctx, list, i);\n const key = get_key(child_ctx);\n let block = lookup.get(key);\n if (!block) {\n block = create_each_block(key, child_ctx);\n block.c();\n }\n else if (dynamic) {\n block.p(child_ctx, dirty);\n }\n new_lookup.set(key, new_blocks[i] = block);\n if (key in old_indexes)\n deltas.set(key, Math.abs(i - old_indexes[key]));\n }\n const will_move = new Set();\n const did_move = new Set();\n function insert(block) {\n transition_in(block, 1);\n block.m(node, next);\n lookup.set(block.key, block);\n next = block.first;\n n--;\n }\n while (o && n) {\n const new_block = new_blocks[n - 1];\n const old_block = old_blocks[o - 1];\n const new_key = new_block.key;\n const old_key = old_block.key;\n if (new_block === old_block) {\n // do nothing\n next = new_block.first;\n o--;\n n--;\n }\n else if (!new_lookup.has(old_key)) {\n // remove old block\n destroy(old_block, lookup);\n o--;\n }\n else if (!lookup.has(new_key) || will_move.has(new_key)) {\n insert(new_block);\n }\n else if (did_move.has(old_key)) {\n o--;\n }\n else if (deltas.get(new_key) > deltas.get(old_key)) {\n did_move.add(new_key);\n insert(new_block);\n }\n else {\n will_move.add(old_key);\n o--;\n }\n }\n while (o--) {\n const old_block = old_blocks[o];\n if (!new_lookup.has(old_block.key))\n destroy(old_block, lookup);\n }\n while (n)\n insert(new_blocks[n - 1]);\n return new_blocks;\n}\nfunction validate_each_keys(ctx, list, get_context, get_key) {\n const keys = new Set();\n for (let i = 0; i < list.length; i++) {\n const key = get_key(get_context(ctx, list, i));\n if (keys.has(key)) {\n throw new Error(`Cannot have duplicate keys in a keyed each`);\n }\n keys.add(key);\n }\n}\n\nfunction get_spread_update(levels, updates) {\n const update = {};\n const to_null_out = {};\n const accounted_for = { $$scope: 1 };\n let i = levels.length;\n while (i--) {\n const o = levels[i];\n const n = updates[i];\n if (n) {\n for (const key in o) {\n if (!(key in n))\n to_null_out[key] = 1;\n }\n for (const key in n) {\n if (!accounted_for[key]) {\n update[key] = n[key];\n accounted_for[key] = 1;\n }\n }\n levels[i] = n;\n }\n else {\n for (const key in o) {\n accounted_for[key] = 1;\n }\n }\n }\n for (const key in to_null_out) {\n if (!(key in update))\n update[key] = undefined;\n }\n return update;\n}\nfunction get_spread_object(spread_props) {\n return typeof spread_props === 'object' && spread_props !== null ? spread_props : {};\n}\n\n// source: https://html.spec.whatwg.org/multipage/indices.html\nconst boolean_attributes = new Set([\n 'allowfullscreen',\n 'allowpaymentrequest',\n 'async',\n 'autofocus',\n 'autoplay',\n 'checked',\n 'controls',\n 'default',\n 'defer',\n 'disabled',\n 'formnovalidate',\n 'hidden',\n 'ismap',\n 'loop',\n 'multiple',\n 'muted',\n 'nomodule',\n 'novalidate',\n 'open',\n 'playsinline',\n 'readonly',\n 'required',\n 'reversed',\n 'selected'\n]);\n\nconst invalid_attribute_name_character = /[\\s'\">/=\\u{FDD0}-\\u{FDEF}\\u{FFFE}\\u{FFFF}\\u{1FFFE}\\u{1FFFF}\\u{2FFFE}\\u{2FFFF}\\u{3FFFE}\\u{3FFFF}\\u{4FFFE}\\u{4FFFF}\\u{5FFFE}\\u{5FFFF}\\u{6FFFE}\\u{6FFFF}\\u{7FFFE}\\u{7FFFF}\\u{8FFFE}\\u{8FFFF}\\u{9FFFE}\\u{9FFFF}\\u{AFFFE}\\u{AFFFF}\\u{BFFFE}\\u{BFFFF}\\u{CFFFE}\\u{CFFFF}\\u{DFFFE}\\u{DFFFF}\\u{EFFFE}\\u{EFFFF}\\u{FFFFE}\\u{FFFFF}\\u{10FFFE}\\u{10FFFF}]/u;\n// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2\n// https://infra.spec.whatwg.org/#noncharacter\nfunction spread(args, classes_to_add) {\n const attributes = Object.assign({}, ...args);\n if (classes_to_add) {\n if (attributes.class == null) {\n attributes.class = classes_to_add;\n }\n else {\n attributes.class += ' ' + classes_to_add;\n }\n }\n let str = '';\n Object.keys(attributes).forEach(name => {\n if (invalid_attribute_name_character.test(name))\n return;\n const value = attributes[name];\n if (value === true)\n str += \" \" + name;\n else if (boolean_attributes.has(name.toLowerCase())) {\n if (value)\n str += \" \" + name;\n }\n else if (value != null) {\n str += ` ${name}=\"${String(value).replace(/\"/g, '"').replace(/'/g, ''')}\"`;\n }\n });\n return str;\n}\nconst escaped = {\n '\"': '"',\n \"'\": ''',\n '&': '&',\n '<': '<',\n '>': '>'\n};\nfunction escape(html) {\n return String(html).replace(/[\"'&<>]/g, match => escaped[match]);\n}\nfunction each(items, fn) {\n let str = '';\n for (let i = 0; i < items.length; i += 1) {\n str += fn(items[i], i);\n }\n return str;\n}\nconst missing_component = {\n $$render: () => ''\n};\nfunction validate_component(component, name) {\n if (!component || !component.$$render) {\n if (name === 'svelte:component')\n name += ' this={...}';\n throw new Error(`<${name}> is not a valid SSR component. You may need to review your build config to ensure that dependencies are compiled, rather than imported as pre-compiled modules`);\n }\n return component;\n}\nfunction debug(file, line, column, values) {\n console.log(`{@debug} ${file ? file + ' ' : ''}(${line}:${column})`); // eslint-disable-line no-console\n console.log(values); // eslint-disable-line no-console\n return '';\n}\nlet on_destroy;\nfunction create_ssr_component(fn) {\n function $$render(result, props, bindings, slots) {\n const parent_component = current_component;\n const $$ = {\n on_destroy,\n context: new Map(parent_component ? parent_component.$$.context : []),\n // these will be immediately discarded\n on_mount: [],\n before_update: [],\n after_update: [],\n callbacks: blank_object()\n };\n set_current_component({ $$ });\n const html = fn(result, props, bindings, slots);\n set_current_component(parent_component);\n return html;\n }\n return {\n render: (props = {}, options = {}) => {\n on_destroy = [];\n const result = { title: '', head: '', css: new Set() };\n const html = $$render(result, props, {}, options);\n run_all(on_destroy);\n return {\n html,\n css: {\n code: Array.from(result.css).map(css => css.code).join('\\n'),\n map: null // TODO\n },\n head: result.title + result.head\n };\n },\n $$render\n };\n}\nfunction add_attribute(name, value, boolean) {\n if (value == null || (boolean && !value))\n return '';\n return ` ${name}${value === true ? '' : `=${typeof value === 'string' ? JSON.stringify(escape(value)) : `\"${value}\"`}`}`;\n}\nfunction add_classes(classes) {\n return classes ? ` class=\"${classes}\"` : ``;\n}\n\nfunction bind(component, name, callback) {\n const index = component.$$.props[name];\n if (index !== undefined) {\n component.$$.bound[index] = callback;\n callback(component.$$.ctx[index]);\n }\n}\nfunction create_component(block) {\n block && block.c();\n}\nfunction claim_component(block, parent_nodes) {\n block && block.l(parent_nodes);\n}\nfunction mount_component(component, target, anchor) {\n const { fragment, on_mount, on_destroy, after_update } = component.$$;\n fragment && fragment.m(target, anchor);\n // onMount happens before the initial afterUpdate\n add_render_callback(() => {\n const new_on_destroy = on_mount.map(run).filter(is_function);\n if (on_destroy) {\n on_destroy.push(...new_on_destroy);\n }\n else {\n // Edge case - component was destroyed immediately,\n // most likely as a result of a binding initialising\n run_all(new_on_destroy);\n }\n component.$$.on_mount = [];\n });\n after_update.forEach(add_render_callback);\n}\nfunction destroy_component(component, detaching) {\n const $$ = component.$$;\n if ($$.fragment !== null) {\n run_all($$.on_destroy);\n $$.fragment && $$.fragment.d(detaching);\n // TODO null out other refs, including component.$$ (but need to\n // preserve final state?)\n $$.on_destroy = $$.fragment = null;\n $$.ctx = [];\n }\n}\nfunction make_dirty(component, i) {\n if (component.$$.dirty[0] === -1) {\n dirty_components.push(component);\n schedule_update();\n component.$$.dirty.fill(0);\n }\n component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31));\n}\nfunction init(component, options, instance, create_fragment, not_equal, props, dirty = [-1]) {\n const parent_component = current_component;\n set_current_component(component);\n const prop_values = options.props || {};\n const $$ = component.$$ = {\n fragment: null,\n ctx: null,\n // state\n props,\n update: noop,\n not_equal,\n bound: blank_object(),\n // lifecycle\n on_mount: [],\n on_destroy: [],\n before_update: [],\n after_update: [],\n context: new Map(parent_component ? parent_component.$$.context : []),\n // everything else\n callbacks: blank_object(),\n dirty\n };\n let ready = false;\n $$.ctx = instance\n ? instance(component, prop_values, (i, ret, ...rest) => {\n const value = rest.length ? rest[0] : ret;\n if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) {\n if ($$.bound[i])\n $$.bound[i](value);\n if (ready)\n make_dirty(component, i);\n }\n return ret;\n })\n : [];\n $$.update();\n ready = true;\n run_all($$.before_update);\n // `false` as a special case of no DOM component\n $$.fragment = create_fragment ? create_fragment($$.ctx) : false;\n if (options.target) {\n if (options.hydrate) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n $$.fragment && $$.fragment.l(children(options.target));\n }\n else {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n $$.fragment && $$.fragment.c();\n }\n if (options.intro)\n transition_in(component.$$.fragment);\n mount_component(component, options.target, options.anchor);\n flush();\n }\n set_current_component(parent_component);\n}\nlet SvelteElement;\nif (typeof HTMLElement === 'function') {\n SvelteElement = class extends HTMLElement {\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n connectedCallback() {\n // @ts-ignore todo: improve typings\n for (const key in this.$$.slotted) {\n // @ts-ignore todo: improve typings\n this.appendChild(this.$$.slotted[key]);\n }\n }\n attributeChangedCallback(attr, _oldValue, newValue) {\n this[attr] = newValue;\n }\n $destroy() {\n destroy_component(this, 1);\n this.$destroy = noop;\n }\n $on(type, callback) {\n // TODO should this delegate to addEventListener?\n const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = []));\n callbacks.push(callback);\n return () => {\n const index = callbacks.indexOf(callback);\n if (index !== -1)\n callbacks.splice(index, 1);\n };\n }\n $set() {\n // overridden by instance, if it has props\n }\n };\n}\nclass SvelteComponent {\n $destroy() {\n destroy_component(this, 1);\n this.$destroy = noop;\n }\n $on(type, callback) {\n const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = []));\n callbacks.push(callback);\n return () => {\n const index = callbacks.indexOf(callback);\n if (index !== -1)\n callbacks.splice(index, 1);\n };\n }\n $set() {\n // overridden by instance, if it has props\n }\n}\n\nfunction dispatch_dev(type, detail) {\n document.dispatchEvent(custom_event(type, Object.assign({ version: '3.18.2' }, detail)));\n}\nfunction append_dev(target, node) {\n dispatch_dev(\"SvelteDOMInsert\", { target, node });\n append(target, node);\n}\nfunction insert_dev(target, node, anchor) {\n dispatch_dev(\"SvelteDOMInsert\", { target, node, anchor });\n insert(target, node, anchor);\n}\nfunction detach_dev(node) {\n dispatch_dev(\"SvelteDOMRemove\", { node });\n detach(node);\n}\nfunction detach_between_dev(before, after) {\n while (before.nextSibling && before.nextSibling !== after) {\n detach_dev(before.nextSibling);\n }\n}\nfunction detach_before_dev(after) {\n while (after.previousSibling) {\n detach_dev(after.previousSibling);\n }\n}\nfunction detach_after_dev(before) {\n while (before.nextSibling) {\n detach_dev(before.nextSibling);\n }\n}\nfunction listen_dev(node, event, handler, options, has_prevent_default, has_stop_propagation) {\n const modifiers = options === true ? [\"capture\"] : options ? Array.from(Object.keys(options)) : [];\n if (has_prevent_default)\n modifiers.push('preventDefault');\n if (has_stop_propagation)\n modifiers.push('stopPropagation');\n dispatch_dev(\"SvelteDOMAddEventListener\", { node, event, handler, modifiers });\n const dispose = listen(node, event, handler, options);\n return () => {\n dispatch_dev(\"SvelteDOMRemoveEventListener\", { node, event, handler, modifiers });\n dispose();\n };\n}\nfunction attr_dev(node, attribute, value) {\n attr(node, attribute, value);\n if (value == null)\n dispatch_dev(\"SvelteDOMRemoveAttribute\", { node, attribute });\n else\n dispatch_dev(\"SvelteDOMSetAttribute\", { node, attribute, value });\n}\nfunction prop_dev(node, property, value) {\n node[property] = value;\n dispatch_dev(\"SvelteDOMSetProperty\", { node, property, value });\n}\nfunction dataset_dev(node, property, value) {\n node.dataset[property] = value;\n dispatch_dev(\"SvelteDOMSetDataset\", { node, property, value });\n}\nfunction set_data_dev(text, data) {\n data = '' + data;\n if (text.data === data)\n return;\n dispatch_dev(\"SvelteDOMSetData\", { node: text, data });\n text.data = data;\n}\nclass SvelteComponentDev extends SvelteComponent {\n constructor(options) {\n if (!options || (!options.target && !options.$$inline)) {\n throw new Error(`'target' is a required option`);\n }\n super();\n }\n $destroy() {\n super.$destroy();\n this.$destroy = () => {\n console.warn(`Component was already destroyed`); // eslint-disable-line no-console\n };\n }\n}\nfunction loop_guard(timeout) {\n const start = Date.now();\n return () => {\n if (Date.now() - start > timeout) {\n throw new Error(`Infinite loop detected`);\n }\n };\n}\n\nexport { HtmlTag, SvelteComponent, SvelteComponentDev, SvelteElement, action_destroyer, add_attribute, add_classes, add_flush_callback, add_location, add_render_callback, add_resize_listener, add_transform, afterUpdate, append, append_dev, assign, attr, attr_dev, beforeUpdate, bind, binding_callbacks, blank_object, bubble, check_outros, children, claim_component, claim_element, claim_space, claim_text, clear_loops, component_subscribe, createEventDispatcher, create_animation, create_bidirectional_transition, create_component, create_in_transition, create_out_transition, create_slot, create_ssr_component, current_component, custom_event, dataset_dev, debug, destroy_block, destroy_component, destroy_each, detach, detach_after_dev, detach_before_dev, detach_between_dev, detach_dev, dirty_components, dispatch_dev, each, element, element_is, empty, escape, escaped, exclude_internal_props, fix_and_destroy_block, fix_and_outro_and_destroy_block, fix_position, flush, getContext, get_binding_group_value, get_current_component, get_slot_changes, get_slot_context, get_spread_object, get_spread_update, get_store_value, globals, group_outros, handle_promise, has_prop, identity, init, insert, insert_dev, intros, invalid_attribute_name_character, is_client, is_function, is_promise, listen, listen_dev, loop, loop_guard, missing_component, mount_component, noop, not_equal, now, null_to_empty, object_without_properties, onDestroy, onMount, once, outro_and_destroy_block, prevent_default, prop_dev, query_selector_all, raf, run, run_all, safe_not_equal, schedule_update, select_multiple_value, select_option, select_options, select_value, self, setContext, set_attributes, set_current_component, set_custom_element_data, set_data, set_data_dev, set_input_type, set_input_value, set_now, set_raf, set_store_value, set_style, set_svg_attributes, space, spread, stop_propagation, subscribe, svg_element, text, tick, time_ranges_to_array, to_number, toggle_class, transition_in, transition_out, update_keyed_each, validate_component, validate_each_keys, validate_store, xlink_attr };\n","import { noop, safe_not_equal, subscribe, run_all, is_function } from '../internal';\nexport { get_store_value as get } from '../internal';\n\nconst subscriber_queue = [];\n/**\n * Creates a `Readable` store that allows reading by subscription.\n * @param value initial value\n * @param {StartStopNotifier}start start and stop notifications for subscriptions\n */\nfunction readable(value, start) {\n return {\n subscribe: writable(value, start).subscribe,\n };\n}\n/**\n * Create a `Writable` store that allows both updating and reading by subscription.\n * @param {*=}value initial value\n * @param {StartStopNotifier=}start start and stop notifications for subscriptions\n */\nfunction writable(value, start = noop) {\n let stop;\n const subscribers = [];\n function set(new_value) {\n if (safe_not_equal(value, new_value)) {\n value = new_value;\n if (stop) { // store is ready\n const run_queue = !subscriber_queue.length;\n for (let i = 0; i < subscribers.length; i += 1) {\n const s = subscribers[i];\n s[1]();\n subscriber_queue.push(s, value);\n }\n if (run_queue) {\n for (let i = 0; i < subscriber_queue.length; i += 2) {\n subscriber_queue[i][0](subscriber_queue[i + 1]);\n }\n subscriber_queue.length = 0;\n }\n }\n }\n }\n function update(fn) {\n set(fn(value));\n }\n function subscribe(run, invalidate = noop) {\n const subscriber = [run, invalidate];\n subscribers.push(subscriber);\n if (subscribers.length === 1) {\n stop = start(set) || noop;\n }\n run(value);\n return () => {\n const index = subscribers.indexOf(subscriber);\n if (index !== -1) {\n subscribers.splice(index, 1);\n }\n if (subscribers.length === 0) {\n stop();\n stop = null;\n }\n };\n }\n return { set, update, subscribe };\n}\nfunction derived(stores, fn, initial_value) {\n const single = !Array.isArray(stores);\n const stores_array = single\n ? [stores]\n : stores;\n const auto = fn.length < 2;\n return readable(initial_value, (set) => {\n let inited = false;\n const values = [];\n let pending = 0;\n let cleanup = noop;\n const sync = () => {\n if (pending) {\n return;\n }\n cleanup();\n const result = fn(single ? values[0] : values, set);\n if (auto) {\n set(result);\n }\n else {\n cleanup = is_function(result) ? result : noop;\n }\n };\n const unsubscribers = stores_array.map((store, i) => subscribe(store, (value) => {\n values[i] = value;\n pending &= ~(1 << i);\n if (inited) {\n sync();\n }\n }, () => {\n pending |= (1 << i);\n }));\n inited = true;\n sync();\n return function stop() {\n run_all(unsubscribers);\n cleanup();\n };\n });\n}\n\nexport { derived, readable, writable };\n","import { writable, Writable } from \"svelte/store\";\n\nimport {\n object,\n array,\n string,\n boolean,\n number,\n union,\n constant,\n} from \"../validation\";\n\nexport const urlHash = writable(\"\");\n\nexport const conversion = writable(\"\");\ntype Interval = \"year\" | \"quarter\" | \"month\" | \"week\" | \"day\";\nexport const interval: Writable = writable(\"month\");\n\nexport const favaAPIValidator = object({\n accountURL: string,\n accounts: array(string),\n baseURL: string,\n currencies: array(string),\n documentTitle: string,\n errors: number,\n favaOptions: object({\n \"auto-reload\": boolean,\n \"currency-column\": number,\n conversion: string,\n interval: string,\n locale: union(string, constant(null)),\n }),\n have_excel: boolean,\n incognito: boolean,\n links: array(string),\n options: object({\n commodities: array(string),\n documents: array(string),\n operating_currency: array(string),\n }),\n pageTitle: string,\n payees: array(string),\n tags: array(string),\n years: array(number),\n});\n\nexport type FavaAPI = ReturnType;\nexport const favaAPI: FavaAPI = {\n accountURL: \"\",\n accounts: [],\n baseURL: \"\",\n currencies: [],\n documentTitle: \"\",\n errors: 0,\n favaOptions: {\n \"auto-reload\": false,\n \"currency-column\": 80,\n conversion: \"at_cost\",\n interval: \"month\",\n locale: null,\n },\n have_excel: false,\n incognito: false,\n links: [],\n pageTitle: \"\",\n payees: [],\n options: {\n commodities: [],\n documents: [],\n operating_currency: [],\n },\n tags: [],\n years: [],\n};\n\nexport const favaAPIStore = writable(favaAPI);\nfavaAPIStore.subscribe(val => {\n Object.assign(favaAPI, val);\n});\n\nexport const filters = writable({\n time: \"\",\n filter: \"\",\n account: \"\",\n});\n\nexport const urlSyncedParams = [\n \"account\",\n \"charts\",\n \"conversion\",\n \"filter\",\n \"interval\",\n \"time\",\n];\n\nexport function closeOverlay() {\n if (window.location.hash) {\n window.history.pushState({}, \"\", \"#\");\n }\n urlHash.set(\"\");\n}\n","import { object, record, string, unknown } from \"./validation\";\nimport { favaAPI } from \"./stores\";\n\n/**\n * Select a single element.\n */\nexport function select(expr: string, con: Document | Element = document) {\n return con.querySelector(expr);\n}\n\n/**\n * Select multiple elements (and convert NodeList to Array).\n */\nexport function selectAll(expr: string, con: Document | Element = document) {\n return Array.from(con.querySelectorAll(expr));\n}\n\nexport function getScriptTagJSON(selector: string): unknown {\n const el = select(selector);\n if (!el) {\n return null;\n }\n return JSON.parse(el.innerHTML);\n}\n\nlet translations: Record;\n\n/**\n * Translate the given string.\n */\nexport function _(text: string): string {\n if (translations === undefined) {\n translations = record(string)(getScriptTagJSON(\"#translations\"));\n }\n return translations[text] || text;\n}\n\n/**\n * Execute the callback of the event of given type is fired on something\n * matching selector.\n */\nexport function delegate(\n element: Element | Document | null,\n type: string,\n selector: string,\n callback: (e: T, c: C) => void\n) {\n if (!element) {\n return;\n }\n element.addEventListener(type, event => {\n let { target } = event;\n if (!target || !(target instanceof Node)) {\n return;\n }\n if (!(target instanceof Element)) {\n target = target.parentNode;\n }\n if (target instanceof Element) {\n const closest = (target as HTMLElement).closest(selector);\n if (closest) {\n callback(event as T, closest as C);\n }\n }\n });\n}\n\n/**\n * Bind an event to element, only run the callback once.\n */\nexport function once(\n element: EventTarget,\n event: string,\n callback: (ev: Event) => void\n) {\n function runOnce(ev: Event) {\n element.removeEventListener(event, runOnce);\n callback.apply(element, [ev]);\n }\n\n element.addEventListener(event, runOnce);\n}\n\nexport function ready() {\n return new Promise(resolve => {\n if (document.readyState !== \"loading\") {\n resolve();\n } else {\n document.addEventListener(\"DOMContentLoaded\", resolve);\n }\n });\n}\n\n/**\n * Handles JSON content for a Promise returned by fetch, also handling an HTTP\n * error status.\n */\nexport function handleJSON(response: Response): Promise {\n if (!response.ok) {\n return Promise.reject(response.statusText);\n }\n return response.json().then(data => {\n if (!data.success) {\n return Promise.reject(data.error);\n }\n return data;\n });\n}\n\n/**\n * Handles text content for a Promise returned by fetch, also handling an HTTP\n * error status.\n */\nexport function handleText(response: Response): Promise {\n if (!response.ok) {\n return Promise.reject(response.statusText);\n }\n return response.text();\n}\n\nexport function fetch(input: string, init = {}) {\n const defaults: RequestInit = {\n credentials: \"same-origin\",\n };\n return window.fetch(input, Object.assign(defaults, init));\n}\n\nconst validateAPIResponse = object({ data: unknown });\n\n/**\n * Get the URL string for one of Fava's reports.\n */\nexport function urlFor(\n report: string,\n params?: Record\n): string {\n let url = `${favaAPI.baseURL}${report}`;\n if (params) {\n const urlParams = new URLSearchParams();\n Object.entries(params).forEach(([key, value]) => {\n urlParams.set(key, value);\n });\n url += `?${urlParams.toString()}`;\n }\n return url;\n}\n\n/**\n * Fetch an API endpoint and convert the JSON data to an object.\n * @param endpoint - the endpoint to fetch\n * @param params - a string to append as params or an object.\n */\nexport async function fetchAPI(\n endpoint: string,\n params?: Record\n): Promise {\n const url = urlFor(`api/${endpoint}`, params);\n const responseData = await fetch(url);\n const json: unknown = await handleJSON(responseData);\n return validateAPIResponse(json).data;\n}\n\nconst putAPIValidators = {\n add_entries: string,\n format_source: string,\n source: string,\n};\n\ntype apiTypes = typeof putAPIValidators;\n\n/**\n * Fetch an API endpoint and convert the JSON data to an object.\n * @param endpoint - the endpoint to fetch\n * @param params - a string to append as params or an object.\n */\nexport async function putAPI(\n endpoint: T,\n body: any\n): Promise> {\n const res = await fetch(`${favaAPI.baseURL}api/${endpoint}/`, {\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n }).then(handleJSON);\n const { data }: { data: unknown } = validateAPIResponse(res);\n return putAPIValidators[endpoint](data) as ReturnType;\n}\n\n/**\n * Fuzzy match a pattern against a string.\n *\n * Returns true if all characters of `pattern` can be found in order in\n * `string`. For lowercase characters in `pattern` match both lower and upper\n * case, for uppercase only an exact match counts.\n */\nexport function fuzzytest(pattern: string, text: string) {\n let pindex = 0;\n for (let index = 0; index < text.length; index += 1) {\n const char = text[index];\n const search = pattern[pindex];\n if (char === search || char.toLowerCase() === search) {\n pindex += 1;\n }\n }\n return pindex === pattern.length;\n}\n\n/**\n * Wrap fuzzy matched characters.\n *\n * Wrap all occurences of characters of `pattern` (in order) in `string` in\n * tags.\n */\nexport function fuzzywrap(pattern: string, text: string) {\n let pindex = 0;\n const result = [];\n for (let index = 0; index < text.length; index += 1) {\n const char = text[index];\n const search = pattern[pindex];\n if (char === search || char.toLowerCase() === search) {\n result.push(`${char}`);\n pindex += 1;\n } else {\n result.push(char);\n }\n }\n return result.join(\"\");\n}\n","/**\n * Minimal event handler\n */\nclass Events {\n events: Record;\n\n constructor() {\n this.events = {};\n }\n\n on(event: string, callback: Function) {\n this.events[event] = this.events[event] || [];\n this.events[event].push(callback);\n }\n\n once(event: string, callback: Function) {\n const runOnce = (arg: any) => {\n this.remove(event, runOnce);\n callback(arg);\n };\n\n this.on(event, runOnce);\n }\n\n remove(event: string, callback: Function) {\n if (!this.events[event].length) {\n return;\n }\n this.events[event] = this.events[event].filter(c => c !== callback);\n }\n\n trigger(event: string, arg?: any) {\n if (!this.events[event]) {\n return;\n }\n this.events[event].forEach(callback => {\n callback(arg);\n });\n }\n}\n\n// This global event handler is used by separate parts of the UI to\n// communicate.\nconst e = new Events();\nexport default e;\n","import { select, delegate } from \"./helpers\";\n\n/*\n * Show a notification containing the given `msg` text and having class `cls`.\n * The notification is automatically removed after 5 seconds and on click\n * `callback` is called.\n *\n * @param {string} msg - The message to diplay\n * @param {string} cls - The message type.\n * @param {function} callback - The callback to execute on click..\n */\nexport function notify(msg: string, cls = \"info\", callback?: Function) {\n const notification = document.createElement(\"li\");\n notification.classList.add(cls);\n notification.appendChild(document.createTextNode(msg));\n const notificationList = select(\"#notifications\");\n if (!notificationList) {\n throw new Error();\n }\n notificationList.append(notification);\n notification.addEventListener(\"click\", () => {\n notification.remove();\n if (callback) {\n callback();\n }\n });\n setTimeout(() => {\n notification.remove();\n }, 5000);\n}\n\ndelegate(select(\"#notifications\"), \"click\", \"li\", (event, closest) => {\n closest.remove();\n});\n","import { writable, derived } from \"svelte/store\";\n\nimport { _ } from \"../helpers\";\nimport { favaAPIStore } from \".\";\n\nexport const showCharts = writable(true);\nexport const activeChart = writable({});\nexport const chartMode = writable(\"treemap\");\nexport const chartCurrency = writable(\"\");\n\nexport const conversions = derived(favaAPIStore, favaAPI => [\n [\"at_cost\", _(\"At Cost\")],\n [\"at_value\", _(\"At Market Value\")],\n [\"units\", _(\"Units\")],\n ...favaAPI.options.operating_currency\n .sort()\n .map(currency => [currency, `Converted to ${currency}`]),\n ...favaAPI.options.commodities\n .sort()\n .filter(\n c => !favaAPI.options.operating_currency.includes(c) && c.length <= 3\n )\n .map(currency => [currency, `Converted to ${currency}`]),\n]);\n// TODO _('Converted to %(currency)s', currency=currency)\n","// Routing\n//\n// Fava intercepts all clicks on links and will in most cases asynchronously\n// load the content of the page and replace the
contents with them.\n\nimport { Writable } from \"svelte/store\";\nimport { select, delegate, fetch, handleText } from \"./helpers\";\nimport e from \"./events\";\nimport { notify } from \"./notifications\";\nimport {\n urlHash,\n conversion,\n interval,\n filters,\n favaAPI,\n urlSyncedParams,\n} from \"./stores\";\nimport { showCharts } from \"./stores/chart\";\n\nclass Router {\n state: {\n hash: string;\n pathname: string;\n search: string;\n };\n\n constructor() {\n this.state = {\n hash: window.location.hash,\n pathname: window.location.pathname,\n search: window.location.search,\n };\n }\n\n // This should be called once when the page has been loaded. Initializes the\n // router and takes over clicking on links.\n init() {\n urlHash.set(window.location.hash.slice(1));\n this.updateState();\n\n window.addEventListener(\"popstate\", () => {\n urlHash.set(window.location.hash.slice(1));\n if (\n window.location.hash !== this.state.hash &&\n window.location.pathname === this.state.pathname &&\n window.location.search === this.state.search\n ) {\n this.updateState();\n } else if (\n window.location.pathname !== this.state.pathname ||\n window.location.search !== this.state.search\n ) {\n this.loadURL(window.location.href, false);\n }\n });\n\n this.takeOverLinks();\n }\n\n // Go to URL. If load is `true`, load the page at URL, otherwise only update\n // the current state.\n navigate(url: string, load = true) {\n if (load) {\n this.loadURL(url);\n } else {\n window.history.pushState(null, \"\", url);\n this.updateState();\n }\n }\n\n /*\n * Replace
contents with the page at `url`.\n *\n * If `historyState` is false, do not create a history state and do not\n * scroll to top.\n */\n async loadURL(url: string, historyState = true) {\n const state = { interrupt: false };\n e.trigger(\"navigate\", state);\n if (state.interrupt) {\n return;\n }\n\n const getUrl = new URL(url);\n getUrl.searchParams.set(\"partial\", \"true\");\n\n const svg = select(\".fava-icon\");\n if (svg) {\n svg.classList.add(\"loading\");\n }\n\n try {\n const content = await fetch(getUrl.toString()).then(handleText);\n if (historyState) {\n window.history.pushState(null, \"\", url);\n window.scroll(0, 0);\n }\n this.updateState();\n const article = select(\"article\");\n if (article) {\n article.innerHTML = content;\n }\n e.trigger(\"page-loaded\");\n urlHash.set(window.location.hash.slice(1));\n } catch (error) {\n notify(`Loading ${url} failed.`, \"error\");\n } finally {\n if (svg) {\n svg.classList.remove(\"loading\");\n }\n }\n }\n\n /*\n * Update the routers state object.\n *\n * The state object is used to distinguish between the user navigating the\n * browser history or the hash changing.\n */\n updateState() {\n this.state = {\n hash: window.location.hash,\n pathname: window.location.pathname,\n search: window.location.search,\n };\n }\n\n /*\n * Intercept all clicks on links () and .navigate() to the link instead.\n *\n * Doesn't intercept if\n * - a button different from the main button is used,\n * - a modifier key is pressed,\n * - the link starts with a hash '#', or\n * - the link has a `data-remote` attribute.\n */\n takeOverLinks() {\n delegate(\n document,\n \"click\",\n \"a\",\n (event: MouseEvent, link: HTMLAnchorElement) => {\n if (\n (link.getAttribute(\"href\") || \"\").charAt(0) === \"#\" ||\n link.host !== window.location.host ||\n link.hasAttribute(\"data-remote\") ||\n link.protocol.indexOf(\"http\") !== 0 ||\n event.defaultPrevented\n ) {\n return;\n }\n // update sidebar links\n if (link.closest(\"aside\")) {\n const newURL = new URL(link.href);\n const oldParams = new URL(window.location.href).searchParams;\n for (const name of urlSyncedParams) {\n const value = oldParams.get(name);\n if (value) {\n newURL.searchParams.set(name, value);\n } else {\n newURL.searchParams.delete(name);\n }\n }\n link.href = newURL.toString();\n }\n if (\n event.button !== 0 ||\n event.altKey ||\n event.ctrlKey ||\n event.metaKey ||\n event.shiftKey\n ) {\n return;\n }\n\n event.preventDefault();\n this.navigate(link.href);\n }\n );\n }\n\n /*\n * Reload the page.\n */\n reload() {\n this.loadURL(window.location.href, false);\n }\n}\n\nconst router = new Router();\nexport default router;\n\ne.on(\"page-init\", () => {\n select(\"#reload-page\")!.addEventListener(\"click\", () => {\n router.reload();\n });\n\n const params = new URL(window.location.href).searchParams;\n\n filters.set({\n time: params.get(\"time\") || \"\",\n filter: params.get(\"filter\") || \"\",\n account: params.get(\"account\") || \"\",\n });\n filters.subscribe(fs => {\n const newURL = new URL(window.location.href);\n for (const name of Object.keys(fs)) {\n const value = fs[name as keyof typeof fs];\n if (value) {\n newURL.searchParams.set(name, value);\n } else {\n newURL.searchParams.delete(name);\n }\n }\n const url = newURL.toString();\n if (url !== window.location.href) {\n router.navigate(url);\n }\n });\n\n function syncStoreValueToUrl(\n store: Writable,\n name: string,\n defaultValue: T,\n shouldLoad: boolean\n ): void {\n let value: T;\n if (typeof defaultValue === \"boolean\") {\n value = (params.get(name) !== \"false\" && defaultValue) as T;\n } else {\n value = (params.get(name) as T) || defaultValue;\n }\n store.set(value);\n\n store.subscribe((val: T) => {\n const newURL = new URL(window.location.href);\n newURL.searchParams.set(name, val.toString());\n if (val === defaultValue) {\n newURL.searchParams.delete(name);\n }\n const url = newURL.toString();\n if (url !== window.location.href) {\n router.navigate(url, shouldLoad);\n }\n });\n }\n\n // Set initial values from URL and update URL on store changes\n syncStoreValueToUrl(interval, \"interval\", favaAPI.favaOptions.interval, true);\n syncStoreValueToUrl(\n conversion,\n \"conversion\",\n favaAPI.favaOptions.conversion,\n true\n );\n syncStoreValueToUrl(showCharts, \"charts\", true, false);\n});\n","export default function(a, b) {\n return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;\n}\n","import ascending from \"./ascending.js\";\n\nexport default function(compare) {\n if (compare.length === 1) compare = ascendingComparator(compare);\n return {\n left: function(a, x, lo, hi) {\n if (lo == null) lo = 0;\n if (hi == null) hi = a.length;\n while (lo < hi) {\n var mid = lo + hi >>> 1;\n if (compare(a[mid], x) < 0) lo = mid + 1;\n else hi = mid;\n }\n return lo;\n },\n right: function(a, x, lo, hi) {\n if (lo == null) lo = 0;\n if (hi == null) hi = a.length;\n while (lo < hi) {\n var mid = lo + hi >>> 1;\n if (compare(a[mid], x) > 0) hi = mid;\n else lo = mid + 1;\n }\n return lo;\n }\n };\n}\n\nfunction ascendingComparator(f) {\n return function(d, x) {\n return ascending(f(d), x);\n };\n}\n","import ascending from \"./ascending.js\";\nimport bisector from \"./bisector.js\";\n\nvar ascendingBisect = bisector(ascending);\nexport var bisectRight = ascendingBisect.right;\nexport var bisectLeft = ascendingBisect.left;\nexport default bisectRight;\n","export default function(values, valueof) {\n let min;\n let max;\n if (valueof === undefined) {\n for (const value of values) {\n if (value != null) {\n if (min === undefined) {\n if (value >= value) min = max = value;\n } else {\n if (min > value) min = value;\n if (max < value) max = value;\n }\n }\n }\n } else {\n let index = -1;\n for (let value of values) {\n if ((value = valueof(value, ++index, values)) != null) {\n if (min === undefined) {\n if (value >= value) min = max = value;\n } else {\n if (min > value) min = value;\n if (max < value) max = value;\n }\n }\n }\n }\n return [min, max];\n}\n","export default function(x) {\n return x;\n}\n","import identity from \"./identity.js\";\n\nexport default function group(values, ...keys) {\n return nest(values, identity, identity, keys);\n}\n\nexport function groups(values, ...keys) {\n return nest(values, Array.from, identity, keys);\n}\n\nexport function rollup(values, reduce, ...keys) {\n return nest(values, identity, reduce, keys);\n}\n\nexport function rollups(values, reduce, ...keys) {\n return nest(values, Array.from, reduce, keys);\n}\n\nfunction nest(values, map, reduce, keys) {\n return (function regroup(values, i) {\n if (i >= keys.length) return reduce(values);\n const groups = new Map();\n const keyof = keys[i++];\n let index = -1;\n for (const value of values) {\n const key = keyof(value, ++index, values);\n const group = groups.get(key);\n if (group) group.push(value);\n else groups.set(key, [value]);\n }\n for (const [key, values] of groups) {\n groups.set(key, regroup(values, i));\n }\n return map(groups);\n })(values, 0);\n}\n","export default function(start, stop, step) {\n start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;\n\n var i = -1,\n n = Math.max(0, Math.ceil((stop - start) / step)) | 0,\n range = new Array(n);\n\n while (++i < n) {\n range[i] = start + i * step;\n }\n\n return range;\n}\n","var e10 = Math.sqrt(50),\n e5 = Math.sqrt(10),\n e2 = Math.sqrt(2);\n\nexport default function(start, stop, count) {\n var reverse,\n i = -1,\n n,\n ticks,\n step;\n\n stop = +stop, start = +start, count = +count;\n if (start === stop && count > 0) return [start];\n if (reverse = stop < start) n = start, start = stop, stop = n;\n if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];\n\n if (step > 0) {\n start = Math.ceil(start / step);\n stop = Math.floor(stop / step);\n ticks = new Array(n = Math.ceil(stop - start + 1));\n while (++i < n) ticks[i] = (start + i) * step;\n } else {\n start = Math.floor(start * step);\n stop = Math.ceil(stop * step);\n ticks = new Array(n = Math.ceil(start - stop + 1));\n while (++i < n) ticks[i] = (start - i) / step;\n }\n\n if (reverse) ticks.reverse();\n\n return ticks;\n}\n\nexport function tickIncrement(start, stop, count) {\n var step = (stop - start) / Math.max(0, count),\n power = Math.floor(Math.log(step) / Math.LN10),\n error = step / Math.pow(10, power);\n return power >= 0\n ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power)\n : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1);\n}\n\nexport function tickStep(start, stop, count) {\n var step0 = Math.abs(stop - start) / Math.max(0, count),\n step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),\n error = step0 / step1;\n if (error >= e10) step1 *= 10;\n else if (error >= e5) step1 *= 5;\n else if (error >= e2) step1 *= 2;\n return stop < start ? -step1 : step1;\n}\n","export default function max(values, valueof) {\n let max;\n if (valueof === undefined) {\n for (const value of values) {\n if (value != null\n && (max < value || (max === undefined && value >= value))) {\n max = value;\n }\n }\n } else {\n let index = -1;\n for (let value of values) {\n if ((value = valueof(value, ++index, values)) != null\n && (max < value || (max === undefined && value >= value))) {\n max = value;\n }\n }\n }\n return max;\n}\n","export default function min(values, valueof) {\n let min;\n if (valueof === undefined) {\n for (const value of values) {\n if (value != null\n && (min > value || (min === undefined && value >= value))) {\n min = value;\n }\n }\n } else {\n let index = -1;\n for (let value of values) {\n if ((value = valueof(value, ++index, values)) != null\n && (min > value || (min === undefined && value >= value))) {\n min = value;\n }\n }\n }\n return min;\n}\n","function* flatten(arrays) {\n for (const array of arrays) {\n yield* array;\n }\n}\n\nexport default function merge(arrays) {\n return Array.from(flatten(arrays));\n}\n","function count(node) {\n var sum = 0,\n children = node.children,\n i = children && children.length;\n if (!i) sum = 1;\n else while (--i >= 0) sum += children[i].value;\n node.value = sum;\n}\n\nexport default function() {\n return this.eachAfter(count);\n}\n","export default function(callback) {\n var node = this, current, next = [node], children, i, n;\n do {\n current = next.reverse(), next = [];\n while (node = current.pop()) {\n callback(node), children = node.children;\n if (children) for (i = 0, n = children.length; i < n; ++i) {\n next.push(children[i]);\n }\n }\n } while (next.length);\n return this;\n}\n","export default function(callback) {\n var node = this, nodes = [node], children, i;\n while (node = nodes.pop()) {\n callback(node), children = node.children;\n if (children) for (i = children.length - 1; i >= 0; --i) {\n nodes.push(children[i]);\n }\n }\n return this;\n}\n","export default function(callback) {\n var node = this, nodes = [node], next = [], children, i, n;\n while (node = nodes.pop()) {\n next.push(node), children = node.children;\n if (children) for (i = 0, n = children.length; i < n; ++i) {\n nodes.push(children[i]);\n }\n }\n while (node = next.pop()) {\n callback(node);\n }\n return this;\n}\n","export default function(value) {\n return this.eachAfter(function(node) {\n var sum = +value(node.data) || 0,\n children = node.children,\n i = children && children.length;\n while (--i >= 0) sum += children[i].value;\n node.value = sum;\n });\n}\n","export default function(compare) {\n return this.eachBefore(function(node) {\n if (node.children) {\n node.children.sort(compare);\n }\n });\n}\n","export default function(end) {\n var start = this,\n ancestor = leastCommonAncestor(start, end),\n nodes = [start];\n while (start !== ancestor) {\n start = start.parent;\n nodes.push(start);\n }\n var k = nodes.length;\n while (end !== ancestor) {\n nodes.splice(k, 0, end);\n end = end.parent;\n }\n return nodes;\n}\n\nfunction leastCommonAncestor(a, b) {\n if (a === b) return a;\n var aNodes = a.ancestors(),\n bNodes = b.ancestors(),\n c = null;\n a = aNodes.pop();\n b = bNodes.pop();\n while (a === b) {\n c = a;\n a = aNodes.pop();\n b = bNodes.pop();\n }\n return c;\n}\n","export default function() {\n var node = this, nodes = [node];\n while (node = node.parent) {\n nodes.push(node);\n }\n return nodes;\n}\n","export default function() {\n var nodes = [];\n this.each(function(node) {\n nodes.push(node);\n });\n return nodes;\n}\n","export default function() {\n var leaves = [];\n this.eachBefore(function(node) {\n if (!node.children) {\n leaves.push(node);\n }\n });\n return leaves;\n}\n","export default function() {\n var root = this, links = [];\n root.each(function(node) {\n if (node !== root) { // Don’t include the root’s parent, if any.\n links.push({source: node.parent, target: node});\n }\n });\n return links;\n}\n","import node_count from \"./count.js\";\nimport node_each from \"./each.js\";\nimport node_eachBefore from \"./eachBefore.js\";\nimport node_eachAfter from \"./eachAfter.js\";\nimport node_sum from \"./sum.js\";\nimport node_sort from \"./sort.js\";\nimport node_path from \"./path.js\";\nimport node_ancestors from \"./ancestors.js\";\nimport node_descendants from \"./descendants.js\";\nimport node_leaves from \"./leaves.js\";\nimport node_links from \"./links.js\";\n\nexport default function hierarchy(data, children) {\n var root = new Node(data),\n valued = +data.value && (root.value = data.value),\n node,\n nodes = [root],\n child,\n childs,\n i,\n n;\n\n if (children == null) children = defaultChildren;\n\n while (node = nodes.pop()) {\n if (valued) node.value = +node.data.value;\n if ((childs = children(node.data)) && (n = childs.length)) {\n node.children = new Array(n);\n for (i = n - 1; i >= 0; --i) {\n nodes.push(child = node.children[i] = new Node(childs[i]));\n child.parent = node;\n child.depth = node.depth + 1;\n }\n }\n }\n\n return root.eachBefore(computeHeight);\n}\n\nfunction node_copy() {\n return hierarchy(this).eachBefore(copyData);\n}\n\nfunction defaultChildren(d) {\n return d.children;\n}\n\nfunction copyData(node) {\n node.data = node.data.data;\n}\n\nexport function computeHeight(node) {\n var height = 0;\n do node.height = height;\n while ((node = node.parent) && (node.height < ++height));\n}\n\nexport function Node(data) {\n this.data = data;\n this.depth =\n this.height = 0;\n this.parent = null;\n}\n\nNode.prototype = hierarchy.prototype = {\n constructor: Node,\n count: node_count,\n each: node_each,\n eachAfter: node_eachAfter,\n eachBefore: node_eachBefore,\n sum: node_sum,\n sort: node_sort,\n path: node_path,\n ancestors: node_ancestors,\n descendants: node_descendants,\n leaves: node_leaves,\n links: node_links,\n copy: node_copy\n};\n","export function optional(f) {\n return f == null ? null : required(f);\n}\n\nexport function required(f) {\n if (typeof f !== \"function\") throw new Error;\n return f;\n}\n","export function constantZero() {\n return 0;\n}\n\nexport default function(x) {\n return function() {\n return x;\n };\n}\n","export default function(node) {\n node.x0 = Math.round(node.x0);\n node.y0 = Math.round(node.y0);\n node.x1 = Math.round(node.x1);\n node.y1 = Math.round(node.y1);\n}\n","export default function(parent, x0, y0, x1, y1) {\n var nodes = parent.children,\n node,\n i = -1,\n n = nodes.length,\n k = parent.value && (x1 - x0) / parent.value;\n\n while (++i < n) {\n node = nodes[i], node.y0 = y0, node.y1 = y1;\n node.x0 = x0, node.x1 = x0 += node.value * k;\n }\n}\n","import roundNode from \"./treemap/round.js\";\nimport treemapDice from \"./treemap/dice.js\";\n\nexport default function() {\n var dx = 1,\n dy = 1,\n padding = 0,\n round = false;\n\n function partition(root) {\n var n = root.height + 1;\n root.x0 =\n root.y0 = padding;\n root.x1 = dx;\n root.y1 = dy / n;\n root.eachBefore(positionNode(dy, n));\n if (round) root.eachBefore(roundNode);\n return root;\n }\n\n function positionNode(dy, n) {\n return function(node) {\n if (node.children) {\n treemapDice(node, node.x0, dy * (node.depth + 1) / n, node.x1, dy * (node.depth + 2) / n);\n }\n var x0 = node.x0,\n y0 = node.y0,\n x1 = node.x1 - padding,\n y1 = node.y1 - padding;\n if (x1 < x0) x0 = x1 = (x0 + x1) / 2;\n if (y1 < y0) y0 = y1 = (y0 + y1) / 2;\n node.x0 = x0;\n node.y0 = y0;\n node.x1 = x1;\n node.y1 = y1;\n };\n }\n\n partition.round = function(x) {\n return arguments.length ? (round = !!x, partition) : round;\n };\n\n partition.size = function(x) {\n return arguments.length ? (dx = +x[0], dy = +x[1], partition) : [dx, dy];\n };\n\n partition.padding = function(x) {\n return arguments.length ? (padding = +x, partition) : padding;\n };\n\n return partition;\n}\n","export default function(parent, x0, y0, x1, y1) {\n var nodes = parent.children,\n node,\n i = -1,\n n = nodes.length,\n k = parent.value && (y1 - y0) / parent.value;\n\n while (++i < n) {\n node = nodes[i], node.x0 = x0, node.x1 = x1;\n node.y0 = y0, node.y1 = y0 += node.value * k;\n }\n}\n","import treemapDice from \"./dice.js\";\nimport treemapSlice from \"./slice.js\";\n\nexport var phi = (1 + Math.sqrt(5)) / 2;\n\nexport function squarifyRatio(ratio, parent, x0, y0, x1, y1) {\n var rows = [],\n nodes = parent.children,\n row,\n nodeValue,\n i0 = 0,\n i1 = 0,\n n = nodes.length,\n dx, dy,\n value = parent.value,\n sumValue,\n minValue,\n maxValue,\n newRatio,\n minRatio,\n alpha,\n beta;\n\n while (i0 < n) {\n dx = x1 - x0, dy = y1 - y0;\n\n // Find the next non-empty node.\n do sumValue = nodes[i1++].value; while (!sumValue && i1 < n);\n minValue = maxValue = sumValue;\n alpha = Math.max(dy / dx, dx / dy) / (value * ratio);\n beta = sumValue * sumValue * alpha;\n minRatio = Math.max(maxValue / beta, beta / minValue);\n\n // Keep adding nodes while the aspect ratio maintains or improves.\n for (; i1 < n; ++i1) {\n sumValue += nodeValue = nodes[i1].value;\n if (nodeValue < minValue) minValue = nodeValue;\n if (nodeValue > maxValue) maxValue = nodeValue;\n beta = sumValue * sumValue * alpha;\n newRatio = Math.max(maxValue / beta, beta / minValue);\n if (newRatio > minRatio) { sumValue -= nodeValue; break; }\n minRatio = newRatio;\n }\n\n // Position and record the row orientation.\n rows.push(row = {value: sumValue, dice: dx < dy, children: nodes.slice(i0, i1)});\n if (row.dice) treemapDice(row, x0, y0, x1, value ? y0 += dy * sumValue / value : y1);\n else treemapSlice(row, x0, y0, value ? x0 += dx * sumValue / value : x1, y1);\n value -= sumValue, i0 = i1;\n }\n\n return rows;\n}\n\nexport default (function custom(ratio) {\n\n function squarify(parent, x0, y0, x1, y1) {\n squarifyRatio(ratio, parent, x0, y0, x1, y1);\n }\n\n squarify.ratio = function(x) {\n return custom((x = +x) > 1 ? x : 1);\n };\n\n return squarify;\n})(phi);\n","import roundNode from \"./round.js\";\nimport squarify from \"./squarify.js\";\nimport {required} from \"../accessors.js\";\nimport constant, {constantZero} from \"../constant.js\";\n\nexport default function() {\n var tile = squarify,\n round = false,\n dx = 1,\n dy = 1,\n paddingStack = [0],\n paddingInner = constantZero,\n paddingTop = constantZero,\n paddingRight = constantZero,\n paddingBottom = constantZero,\n paddingLeft = constantZero;\n\n function treemap(root) {\n root.x0 =\n root.y0 = 0;\n root.x1 = dx;\n root.y1 = dy;\n root.eachBefore(positionNode);\n paddingStack = [0];\n if (round) root.eachBefore(roundNode);\n return root;\n }\n\n function positionNode(node) {\n var p = paddingStack[node.depth],\n x0 = node.x0 + p,\n y0 = node.y0 + p,\n x1 = node.x1 - p,\n y1 = node.y1 - p;\n if (x1 < x0) x0 = x1 = (x0 + x1) / 2;\n if (y1 < y0) y0 = y1 = (y0 + y1) / 2;\n node.x0 = x0;\n node.y0 = y0;\n node.x1 = x1;\n node.y1 = y1;\n if (node.children) {\n p = paddingStack[node.depth + 1] = paddingInner(node) / 2;\n x0 += paddingLeft(node) - p;\n y0 += paddingTop(node) - p;\n x1 -= paddingRight(node) - p;\n y1 -= paddingBottom(node) - p;\n if (x1 < x0) x0 = x1 = (x0 + x1) / 2;\n if (y1 < y0) y0 = y1 = (y0 + y1) / 2;\n tile(node, x0, y0, x1, y1);\n }\n }\n\n treemap.round = function(x) {\n return arguments.length ? (round = !!x, treemap) : round;\n };\n\n treemap.size = function(x) {\n return arguments.length ? (dx = +x[0], dy = +x[1], treemap) : [dx, dy];\n };\n\n treemap.tile = function(x) {\n return arguments.length ? (tile = required(x), treemap) : tile;\n };\n\n treemap.padding = function(x) {\n return arguments.length ? treemap.paddingInner(x).paddingOuter(x) : treemap.paddingInner();\n };\n\n treemap.paddingInner = function(x) {\n return arguments.length ? (paddingInner = typeof x === \"function\" ? x : constant(+x), treemap) : paddingInner;\n };\n\n treemap.paddingOuter = function(x) {\n return arguments.length ? treemap.paddingTop(x).paddingRight(x).paddingBottom(x).paddingLeft(x) : treemap.paddingTop();\n };\n\n treemap.paddingTop = function(x) {\n return arguments.length ? (paddingTop = typeof x === \"function\" ? x : constant(+x), treemap) : paddingTop;\n };\n\n treemap.paddingRight = function(x) {\n return arguments.length ? (paddingRight = typeof x === \"function\" ? x : constant(+x), treemap) : paddingRight;\n };\n\n treemap.paddingBottom = function(x) {\n return arguments.length ? (paddingBottom = typeof x === \"function\" ? x : constant(+x), treemap) : paddingBottom;\n };\n\n treemap.paddingLeft = function(x) {\n return arguments.length ? (paddingLeft = typeof x === \"function\" ? x : constant(+x), treemap) : paddingLeft;\n };\n\n return treemap;\n}\n","export var xhtml = \"http://www.w3.org/1999/xhtml\";\n\nexport default {\n svg: \"http://www.w3.org/2000/svg\",\n xhtml: xhtml,\n xlink: \"http://www.w3.org/1999/xlink\",\n xml: \"http://www.w3.org/XML/1998/namespace\",\n xmlns: \"http://www.w3.org/2000/xmlns/\"\n};\n","import namespaces from \"./namespaces\";\n\nexport default function(name) {\n var prefix = name += \"\", i = prefix.indexOf(\":\");\n if (i >= 0 && (prefix = name.slice(0, i)) !== \"xmlns\") name = name.slice(i + 1);\n return namespaces.hasOwnProperty(prefix) ? {space: namespaces[prefix], local: name} : name;\n}\n","import namespace from \"./namespace\";\nimport {xhtml} from \"./namespaces\";\n\nfunction creatorInherit(name) {\n return function() {\n var document = this.ownerDocument,\n uri = this.namespaceURI;\n return uri === xhtml && document.documentElement.namespaceURI === xhtml\n ? document.createElement(name)\n : document.createElementNS(uri, name);\n };\n}\n\nfunction creatorFixed(fullname) {\n return function() {\n return this.ownerDocument.createElementNS(fullname.space, fullname.local);\n };\n}\n\nexport default function(name) {\n var fullname = namespace(name);\n return (fullname.local\n ? creatorFixed\n : creatorInherit)(fullname);\n}\n","function none() {}\n\nexport default function(selector) {\n return selector == null ? none : function() {\n return this.querySelector(selector);\n };\n}\n","import {Selection} from \"./index\";\nimport selector from \"../selector\";\n\nexport default function(select) {\n if (typeof select !== \"function\") select = selector(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {\n if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {\n if (\"__data__\" in node) subnode.__data__ = node.__data__;\n subgroup[i] = subnode;\n }\n }\n }\n\n return new Selection(subgroups, this._parents);\n}\n","function empty() {\n return [];\n}\n\nexport default function(selector) {\n return selector == null ? empty : function() {\n return this.querySelectorAll(selector);\n };\n}\n","import {Selection} from \"./index\";\nimport selectorAll from \"../selectorAll\";\n\nexport default function(select) {\n if (typeof select !== \"function\") select = selectorAll(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n subgroups.push(select.call(node, node.__data__, i, group));\n parents.push(node);\n }\n }\n }\n\n return new Selection(subgroups, parents);\n}\n","export default function(selector) {\n return function() {\n return this.matches(selector);\n };\n}\n","import {Selection} from \"./index\";\nimport matcher from \"../matcher\";\n\nexport default function(match) {\n if (typeof match !== \"function\") match = matcher(match);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {\n if ((node = group[i]) && match.call(node, node.__data__, i, group)) {\n subgroup.push(node);\n }\n }\n }\n\n return new Selection(subgroups, this._parents);\n}\n","export default function(update) {\n return new Array(update.length);\n}\n","import sparse from \"./sparse\";\nimport {Selection} from \"./index\";\n\nexport default function() {\n return new Selection(this._enter || this._groups.map(sparse), this._parents);\n}\n\nexport function EnterNode(parent, datum) {\n this.ownerDocument = parent.ownerDocument;\n this.namespaceURI = parent.namespaceURI;\n this._next = null;\n this._parent = parent;\n this.__data__ = datum;\n}\n\nEnterNode.prototype = {\n constructor: EnterNode,\n appendChild: function(child) { return this._parent.insertBefore(child, this._next); },\n insertBefore: function(child, next) { return this._parent.insertBefore(child, next); },\n querySelector: function(selector) { return this._parent.querySelector(selector); },\n querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); }\n};\n","export default function(x) {\n return function() {\n return x;\n };\n}\n","import {Selection} from \"./index\";\nimport {EnterNode} from \"./enter\";\nimport constant from \"../constant\";\n\nvar keyPrefix = \"$\"; // Protect against keys like “__proto__”.\n\nfunction bindIndex(parent, group, enter, update, exit, data) {\n var i = 0,\n node,\n groupLength = group.length,\n dataLength = data.length;\n\n // Put any non-null nodes that fit into update.\n // Put any null nodes into enter.\n // Put any remaining data into enter.\n for (; i < dataLength; ++i) {\n if (node = group[i]) {\n node.__data__ = data[i];\n update[i] = node;\n } else {\n enter[i] = new EnterNode(parent, data[i]);\n }\n }\n\n // Put any non-null nodes that don’t fit into exit.\n for (; i < groupLength; ++i) {\n if (node = group[i]) {\n exit[i] = node;\n }\n }\n}\n\nfunction bindKey(parent, group, enter, update, exit, data, key) {\n var i,\n node,\n nodeByKeyValue = {},\n groupLength = group.length,\n dataLength = data.length,\n keyValues = new Array(groupLength),\n keyValue;\n\n // Compute the key for each node.\n // If multiple nodes have the same key, the duplicates are added to exit.\n for (i = 0; i < groupLength; ++i) {\n if (node = group[i]) {\n keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group);\n if (keyValue in nodeByKeyValue) {\n exit[i] = node;\n } else {\n nodeByKeyValue[keyValue] = node;\n }\n }\n }\n\n // Compute the key for each datum.\n // If there a node associated with this key, join and add it to update.\n // If there is not (or the key is a duplicate), add it to enter.\n for (i = 0; i < dataLength; ++i) {\n keyValue = keyPrefix + key.call(parent, data[i], i, data);\n if (node = nodeByKeyValue[keyValue]) {\n update[i] = node;\n node.__data__ = data[i];\n nodeByKeyValue[keyValue] = null;\n } else {\n enter[i] = new EnterNode(parent, data[i]);\n }\n }\n\n // Add any remaining nodes that were not bound to data to exit.\n for (i = 0; i < groupLength; ++i) {\n if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) {\n exit[i] = node;\n }\n }\n}\n\nexport default function(value, key) {\n if (!value) {\n data = new Array(this.size()), j = -1;\n this.each(function(d) { data[++j] = d; });\n return data;\n }\n\n var bind = key ? bindKey : bindIndex,\n parents = this._parents,\n groups = this._groups;\n\n if (typeof value !== \"function\") value = constant(value);\n\n for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {\n var parent = parents[j],\n group = groups[j],\n groupLength = group.length,\n data = value.call(parent, parent && parent.__data__, j, parents),\n dataLength = data.length,\n enterGroup = enter[j] = new Array(dataLength),\n updateGroup = update[j] = new Array(dataLength),\n exitGroup = exit[j] = new Array(groupLength);\n\n bind(parent, group, enterGroup, updateGroup, exitGroup, data, key);\n\n // Now connect the enter nodes to their following update node, such that\n // appendChild can insert the materialized enter node before this node,\n // rather than at the end of the parent node.\n for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {\n if (previous = enterGroup[i0]) {\n if (i0 >= i1) i1 = i0 + 1;\n while (!(next = updateGroup[i1]) && ++i1 < dataLength);\n previous._next = next || null;\n }\n }\n }\n\n update = new Selection(update, parents);\n update._enter = enter;\n update._exit = exit;\n return update;\n}\n","import sparse from \"./sparse\";\nimport {Selection} from \"./index\";\n\nexport default function() {\n return new Selection(this._exit || this._groups.map(sparse), this._parents);\n}\n","export default function(onenter, onupdate, onexit) {\n var enter = this.enter(), update = this, exit = this.exit();\n enter = typeof onenter === \"function\" ? onenter(enter) : enter.append(onenter + \"\");\n if (onupdate != null) update = onupdate(update);\n if (onexit == null) exit.remove(); else onexit(exit);\n return enter && update ? enter.merge(update).order() : update;\n}\n","import {Selection} from \"./index\";\n\nexport default function(selection) {\n\n for (var groups0 = this._groups, groups1 = selection._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {\n for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {\n if (node = group0[i] || group1[i]) {\n merge[i] = node;\n }\n }\n }\n\n for (; j < m0; ++j) {\n merges[j] = groups0[j];\n }\n\n return new Selection(merges, this._parents);\n}\n","export default function() {\n\n for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {\n for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {\n if (node = group[i]) {\n if (next && node.compareDocumentPosition(next) ^ 4) next.parentNode.insertBefore(node, next);\n next = node;\n }\n }\n }\n\n return this;\n}\n","import {Selection} from \"./index\";\n\nexport default function(compare) {\n if (!compare) compare = ascending;\n\n function compareNode(a, b) {\n return a && b ? compare(a.__data__, b.__data__) : !a - !b;\n }\n\n for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n sortgroup[i] = node;\n }\n }\n sortgroup.sort(compareNode);\n }\n\n return new Selection(sortgroups, this._parents).order();\n}\n\nfunction ascending(a, b) {\n return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;\n}\n","export default function() {\n var callback = arguments[0];\n arguments[0] = this;\n callback.apply(null, arguments);\n return this;\n}\n","export default function() {\n var nodes = new Array(this.size()), i = -1;\n this.each(function() { nodes[++i] = this; });\n return nodes;\n}\n","export default function() {\n\n for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {\n for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {\n var node = group[i];\n if (node) return node;\n }\n }\n\n return null;\n}\n","export default function() {\n var size = 0;\n this.each(function() { ++size; });\n return size;\n}\n","export default function() {\n return !this.node();\n}\n","export default function(callback) {\n\n for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {\n for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {\n if (node = group[i]) callback.call(node, node.__data__, i, group);\n }\n }\n\n return this;\n}\n","import namespace from \"../namespace\";\n\nfunction attrRemove(name) {\n return function() {\n this.removeAttribute(name);\n };\n}\n\nfunction attrRemoveNS(fullname) {\n return function() {\n this.removeAttributeNS(fullname.space, fullname.local);\n };\n}\n\nfunction attrConstant(name, value) {\n return function() {\n this.setAttribute(name, value);\n };\n}\n\nfunction attrConstantNS(fullname, value) {\n return function() {\n this.setAttributeNS(fullname.space, fullname.local, value);\n };\n}\n\nfunction attrFunction(name, value) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) this.removeAttribute(name);\n else this.setAttribute(name, v);\n };\n}\n\nfunction attrFunctionNS(fullname, value) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) this.removeAttributeNS(fullname.space, fullname.local);\n else this.setAttributeNS(fullname.space, fullname.local, v);\n };\n}\n\nexport default function(name, value) {\n var fullname = namespace(name);\n\n if (arguments.length < 2) {\n var node = this.node();\n return fullname.local\n ? node.getAttributeNS(fullname.space, fullname.local)\n : node.getAttribute(fullname);\n }\n\n return this.each((value == null\n ? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === \"function\"\n ? (fullname.local ? attrFunctionNS : attrFunction)\n : (fullname.local ? attrConstantNS : attrConstant)))(fullname, value));\n}\n","export default function(node) {\n return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node\n || (node.document && node) // node is a Window\n || node.defaultView; // node is a Document\n}\n","import defaultView from \"../window\";\n\nfunction styleRemove(name) {\n return function() {\n this.style.removeProperty(name);\n };\n}\n\nfunction styleConstant(name, value, priority) {\n return function() {\n this.style.setProperty(name, value, priority);\n };\n}\n\nfunction styleFunction(name, value, priority) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) this.style.removeProperty(name);\n else this.style.setProperty(name, v, priority);\n };\n}\n\nexport default function(name, value, priority) {\n return arguments.length > 1\n ? this.each((value == null\n ? styleRemove : typeof value === \"function\"\n ? styleFunction\n : styleConstant)(name, value, priority == null ? \"\" : priority))\n : styleValue(this.node(), name);\n}\n\nexport function styleValue(node, name) {\n return node.style.getPropertyValue(name)\n || defaultView(node).getComputedStyle(node, null).getPropertyValue(name);\n}\n","function propertyRemove(name) {\n return function() {\n delete this[name];\n };\n}\n\nfunction propertyConstant(name, value) {\n return function() {\n this[name] = value;\n };\n}\n\nfunction propertyFunction(name, value) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) delete this[name];\n else this[name] = v;\n };\n}\n\nexport default function(name, value) {\n return arguments.length > 1\n ? this.each((value == null\n ? propertyRemove : typeof value === \"function\"\n ? propertyFunction\n : propertyConstant)(name, value))\n : this.node()[name];\n}\n","function classArray(string) {\n return string.trim().split(/^|\\s+/);\n}\n\nfunction classList(node) {\n return node.classList || new ClassList(node);\n}\n\nfunction ClassList(node) {\n this._node = node;\n this._names = classArray(node.getAttribute(\"class\") || \"\");\n}\n\nClassList.prototype = {\n add: function(name) {\n var i = this._names.indexOf(name);\n if (i < 0) {\n this._names.push(name);\n this._node.setAttribute(\"class\", this._names.join(\" \"));\n }\n },\n remove: function(name) {\n var i = this._names.indexOf(name);\n if (i >= 0) {\n this._names.splice(i, 1);\n this._node.setAttribute(\"class\", this._names.join(\" \"));\n }\n },\n contains: function(name) {\n return this._names.indexOf(name) >= 0;\n }\n};\n\nfunction classedAdd(node, names) {\n var list = classList(node), i = -1, n = names.length;\n while (++i < n) list.add(names[i]);\n}\n\nfunction classedRemove(node, names) {\n var list = classList(node), i = -1, n = names.length;\n while (++i < n) list.remove(names[i]);\n}\n\nfunction classedTrue(names) {\n return function() {\n classedAdd(this, names);\n };\n}\n\nfunction classedFalse(names) {\n return function() {\n classedRemove(this, names);\n };\n}\n\nfunction classedFunction(names, value) {\n return function() {\n (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);\n };\n}\n\nexport default function(name, value) {\n var names = classArray(name + \"\");\n\n if (arguments.length < 2) {\n var list = classList(this.node()), i = -1, n = names.length;\n while (++i < n) if (!list.contains(names[i])) return false;\n return true;\n }\n\n return this.each((typeof value === \"function\"\n ? classedFunction : value\n ? classedTrue\n : classedFalse)(names, value));\n}\n","function textRemove() {\n this.textContent = \"\";\n}\n\nfunction textConstant(value) {\n return function() {\n this.textContent = value;\n };\n}\n\nfunction textFunction(value) {\n return function() {\n var v = value.apply(this, arguments);\n this.textContent = v == null ? \"\" : v;\n };\n}\n\nexport default function(value) {\n return arguments.length\n ? this.each(value == null\n ? textRemove : (typeof value === \"function\"\n ? textFunction\n : textConstant)(value))\n : this.node().textContent;\n}\n","function htmlRemove() {\n this.innerHTML = \"\";\n}\n\nfunction htmlConstant(value) {\n return function() {\n this.innerHTML = value;\n };\n}\n\nfunction htmlFunction(value) {\n return function() {\n var v = value.apply(this, arguments);\n this.innerHTML = v == null ? \"\" : v;\n };\n}\n\nexport default function(value) {\n return arguments.length\n ? this.each(value == null\n ? htmlRemove : (typeof value === \"function\"\n ? htmlFunction\n : htmlConstant)(value))\n : this.node().innerHTML;\n}\n","function raise() {\n if (this.nextSibling) this.parentNode.appendChild(this);\n}\n\nexport default function() {\n return this.each(raise);\n}\n","function lower() {\n if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);\n}\n\nexport default function() {\n return this.each(lower);\n}\n","import creator from \"../creator\";\n\nexport default function(name) {\n var create = typeof name === \"function\" ? name : creator(name);\n return this.select(function() {\n return this.appendChild(create.apply(this, arguments));\n });\n}\n","import creator from \"../creator\";\nimport selector from \"../selector\";\n\nfunction constantNull() {\n return null;\n}\n\nexport default function(name, before) {\n var create = typeof name === \"function\" ? name : creator(name),\n select = before == null ? constantNull : typeof before === \"function\" ? before : selector(before);\n return this.select(function() {\n return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);\n });\n}\n","function remove() {\n var parent = this.parentNode;\n if (parent) parent.removeChild(this);\n}\n\nexport default function() {\n return this.each(remove);\n}\n","function selection_cloneShallow() {\n var clone = this.cloneNode(false), parent = this.parentNode;\n return parent ? parent.insertBefore(clone, this.nextSibling) : clone;\n}\n\nfunction selection_cloneDeep() {\n var clone = this.cloneNode(true), parent = this.parentNode;\n return parent ? parent.insertBefore(clone, this.nextSibling) : clone;\n}\n\nexport default function(deep) {\n return this.select(deep ? selection_cloneDeep : selection_cloneShallow);\n}\n","export default function(value) {\n return arguments.length\n ? this.property(\"__data__\", value)\n : this.node().__data__;\n}\n","var filterEvents = {};\n\nexport var event = null;\n\nif (typeof document !== \"undefined\") {\n var element = document.documentElement;\n if (!(\"onmouseenter\" in element)) {\n filterEvents = {mouseenter: \"mouseover\", mouseleave: \"mouseout\"};\n }\n}\n\nfunction filterContextListener(listener, index, group) {\n listener = contextListener(listener, index, group);\n return function(event) {\n var related = event.relatedTarget;\n if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) {\n listener.call(this, event);\n }\n };\n}\n\nfunction contextListener(listener, index, group) {\n return function(event1) {\n var event0 = event; // Events can be reentrant (e.g., focus).\n event = event1;\n try {\n listener.call(this, this.__data__, index, group);\n } finally {\n event = event0;\n }\n };\n}\n\nfunction parseTypenames(typenames) {\n return typenames.trim().split(/^|\\s+/).map(function(t) {\n var name = \"\", i = t.indexOf(\".\");\n if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);\n return {type: t, name: name};\n });\n}\n\nfunction onRemove(typename) {\n return function() {\n var on = this.__on;\n if (!on) return;\n for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {\n if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {\n this.removeEventListener(o.type, o.listener, o.capture);\n } else {\n on[++i] = o;\n }\n }\n if (++i) on.length = i;\n else delete this.__on;\n };\n}\n\nfunction onAdd(typename, value, capture) {\n var wrap = filterEvents.hasOwnProperty(typename.type) ? filterContextListener : contextListener;\n return function(d, i, group) {\n var on = this.__on, o, listener = wrap(value, i, group);\n if (on) for (var j = 0, m = on.length; j < m; ++j) {\n if ((o = on[j]).type === typename.type && o.name === typename.name) {\n this.removeEventListener(o.type, o.listener, o.capture);\n this.addEventListener(o.type, o.listener = listener, o.capture = capture);\n o.value = value;\n return;\n }\n }\n this.addEventListener(typename.type, listener, capture);\n o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture};\n if (!on) this.__on = [o];\n else on.push(o);\n };\n}\n\nexport default function(typename, value, capture) {\n var typenames = parseTypenames(typename + \"\"), i, n = typenames.length, t;\n\n if (arguments.length < 2) {\n var on = this.node().__on;\n if (on) for (var j = 0, m = on.length, o; j < m; ++j) {\n for (i = 0, o = on[j]; i < n; ++i) {\n if ((t = typenames[i]).type === o.type && t.name === o.name) {\n return o.value;\n }\n }\n }\n return;\n }\n\n on = value ? onAdd : onRemove;\n if (capture == null) capture = false;\n for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture));\n return this;\n}\n\nexport function customEvent(event1, listener, that, args) {\n var event0 = event;\n event1.sourceEvent = event;\n event = event1;\n try {\n return listener.apply(that, args);\n } finally {\n event = event0;\n }\n}\n","import defaultView from \"../window\";\n\nfunction dispatchEvent(node, type, params) {\n var window = defaultView(node),\n event = window.CustomEvent;\n\n if (typeof event === \"function\") {\n event = new event(type, params);\n } else {\n event = window.document.createEvent(\"Event\");\n if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;\n else event.initEvent(type, false, false);\n }\n\n node.dispatchEvent(event);\n}\n\nfunction dispatchConstant(type, params) {\n return function() {\n return dispatchEvent(this, type, params);\n };\n}\n\nfunction dispatchFunction(type, params) {\n return function() {\n return dispatchEvent(this, type, params.apply(this, arguments));\n };\n}\n\nexport default function(type, params) {\n return this.each((typeof params === \"function\"\n ? dispatchFunction\n : dispatchConstant)(type, params));\n}\n","import selection_select from \"./select\";\nimport selection_selectAll from \"./selectAll\";\nimport selection_filter from \"./filter\";\nimport selection_data from \"./data\";\nimport selection_enter from \"./enter\";\nimport selection_exit from \"./exit\";\nimport selection_join from \"./join\";\nimport selection_merge from \"./merge\";\nimport selection_order from \"./order\";\nimport selection_sort from \"./sort\";\nimport selection_call from \"./call\";\nimport selection_nodes from \"./nodes\";\nimport selection_node from \"./node\";\nimport selection_size from \"./size\";\nimport selection_empty from \"./empty\";\nimport selection_each from \"./each\";\nimport selection_attr from \"./attr\";\nimport selection_style from \"./style\";\nimport selection_property from \"./property\";\nimport selection_classed from \"./classed\";\nimport selection_text from \"./text\";\nimport selection_html from \"./html\";\nimport selection_raise from \"./raise\";\nimport selection_lower from \"./lower\";\nimport selection_append from \"./append\";\nimport selection_insert from \"./insert\";\nimport selection_remove from \"./remove\";\nimport selection_clone from \"./clone\";\nimport selection_datum from \"./datum\";\nimport selection_on from \"./on\";\nimport selection_dispatch from \"./dispatch\";\n\nexport var root = [null];\n\nexport function Selection(groups, parents) {\n this._groups = groups;\n this._parents = parents;\n}\n\nfunction selection() {\n return new Selection([[document.documentElement]], root);\n}\n\nSelection.prototype = selection.prototype = {\n constructor: Selection,\n select: selection_select,\n selectAll: selection_selectAll,\n filter: selection_filter,\n data: selection_data,\n enter: selection_enter,\n exit: selection_exit,\n join: selection_join,\n merge: selection_merge,\n order: selection_order,\n sort: selection_sort,\n call: selection_call,\n nodes: selection_nodes,\n node: selection_node,\n size: selection_size,\n empty: selection_empty,\n each: selection_each,\n attr: selection_attr,\n style: selection_style,\n property: selection_property,\n classed: selection_classed,\n text: selection_text,\n html: selection_html,\n raise: selection_raise,\n lower: selection_lower,\n append: selection_append,\n insert: selection_insert,\n remove: selection_remove,\n clone: selection_clone,\n datum: selection_datum,\n on: selection_on,\n dispatch: selection_dispatch\n};\n\nexport default selection;\n","import {Selection, root} from \"./selection/index\";\n\nexport default function(selector) {\n return typeof selector === \"string\"\n ? new Selection([[document.querySelector(selector)]], [document.documentElement])\n : new Selection([[selector]], root);\n}\n","export default function(node, event) {\n var svg = node.ownerSVGElement || node;\n\n if (svg.createSVGPoint) {\n var point = svg.createSVGPoint();\n point.x = event.clientX, point.y = event.clientY;\n point = point.matrixTransform(node.getScreenCTM().inverse());\n return [point.x, point.y];\n }\n\n var rect = node.getBoundingClientRect();\n return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];\n}\n","var noop = {value: function() {}};\n\nfunction dispatch() {\n for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {\n if (!(t = arguments[i] + \"\") || (t in _) || /[\\s.]/.test(t)) throw new Error(\"illegal type: \" + t);\n _[t] = [];\n }\n return new Dispatch(_);\n}\n\nfunction Dispatch(_) {\n this._ = _;\n}\n\nfunction parseTypenames(typenames, types) {\n return typenames.trim().split(/^|\\s+/).map(function(t) {\n var name = \"\", i = t.indexOf(\".\");\n if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);\n if (t && !types.hasOwnProperty(t)) throw new Error(\"unknown type: \" + t);\n return {type: t, name: name};\n });\n}\n\nDispatch.prototype = dispatch.prototype = {\n constructor: Dispatch,\n on: function(typename, callback) {\n var _ = this._,\n T = parseTypenames(typename + \"\", _),\n t,\n i = -1,\n n = T.length;\n\n // If no callback was specified, return the callback of the given type and name.\n if (arguments.length < 2) {\n while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t;\n return;\n }\n\n // If a type was specified, set the callback for the given type and name.\n // Otherwise, if a null callback was specified, remove callbacks of the given name.\n if (callback != null && typeof callback !== \"function\") throw new Error(\"invalid callback: \" + callback);\n while (++i < n) {\n if (t = (typename = T[i]).type) _[t] = set(_[t], typename.name, callback);\n else if (callback == null) for (t in _) _[t] = set(_[t], typename.name, null);\n }\n\n return this;\n },\n copy: function() {\n var copy = {}, _ = this._;\n for (var t in _) copy[t] = _[t].slice();\n return new Dispatch(copy);\n },\n call: function(type, that) {\n if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2];\n if (!this._.hasOwnProperty(type)) throw new Error(\"unknown type: \" + type);\n for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);\n },\n apply: function(type, that, args) {\n if (!this._.hasOwnProperty(type)) throw new Error(\"unknown type: \" + type);\n for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);\n }\n};\n\nfunction get(type, name) {\n for (var i = 0, n = type.length, c; i < n; ++i) {\n if ((c = type[i]).name === name) {\n return c.value;\n }\n }\n}\n\nfunction set(type, name, callback) {\n for (var i = 0, n = type.length; i < n; ++i) {\n if (type[i].name === name) {\n type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1));\n break;\n }\n }\n if (callback != null) type.push({name: name, value: callback});\n return type;\n}\n\nexport default dispatch;\n","var frame = 0, // is an animation frame pending?\n timeout = 0, // is a timeout pending?\n interval = 0, // are any timers active?\n pokeDelay = 1000, // how frequently we check for clock skew\n taskHead,\n taskTail,\n clockLast = 0,\n clockNow = 0,\n clockSkew = 0,\n clock = typeof performance === \"object\" && performance.now ? performance : Date,\n setFrame = typeof window === \"object\" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function(f) { setTimeout(f, 17); };\n\nexport function now() {\n return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);\n}\n\nfunction clearNow() {\n clockNow = 0;\n}\n\nexport function Timer() {\n this._call =\n this._time =\n this._next = null;\n}\n\nTimer.prototype = timer.prototype = {\n constructor: Timer,\n restart: function(callback, delay, time) {\n if (typeof callback !== \"function\") throw new TypeError(\"callback is not a function\");\n time = (time == null ? now() : +time) + (delay == null ? 0 : +delay);\n if (!this._next && taskTail !== this) {\n if (taskTail) taskTail._next = this;\n else taskHead = this;\n taskTail = this;\n }\n this._call = callback;\n this._time = time;\n sleep();\n },\n stop: function() {\n if (this._call) {\n this._call = null;\n this._time = Infinity;\n sleep();\n }\n }\n};\n\nexport function timer(callback, delay, time) {\n var t = new Timer;\n t.restart(callback, delay, time);\n return t;\n}\n\nexport function timerFlush() {\n now(); // Get the current time, if not already set.\n ++frame; // Pretend we’ve set an alarm, if we haven’t already.\n var t = taskHead, e;\n while (t) {\n if ((e = clockNow - t._time) >= 0) t._call.call(null, e);\n t = t._next;\n }\n --frame;\n}\n\nfunction wake() {\n clockNow = (clockLast = clock.now()) + clockSkew;\n frame = timeout = 0;\n try {\n timerFlush();\n } finally {\n frame = 0;\n nap();\n clockNow = 0;\n }\n}\n\nfunction poke() {\n var now = clock.now(), delay = now - clockLast;\n if (delay > pokeDelay) clockSkew -= delay, clockLast = now;\n}\n\nfunction nap() {\n var t0, t1 = taskHead, t2, time = Infinity;\n while (t1) {\n if (t1._call) {\n if (time > t1._time) time = t1._time;\n t0 = t1, t1 = t1._next;\n } else {\n t2 = t1._next, t1._next = null;\n t1 = t0 ? t0._next = t2 : taskHead = t2;\n }\n }\n taskTail = t0;\n sleep(time);\n}\n\nfunction sleep(time) {\n if (frame) return; // Soonest alarm already set, or will be.\n if (timeout) timeout = clearTimeout(timeout);\n var delay = time - clockNow; // Strictly less than if we recomputed clockNow.\n if (delay > 24) {\n if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew);\n if (interval) interval = clearInterval(interval);\n } else {\n if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay);\n frame = 1, setFrame(wake);\n }\n}\n","import {Timer} from \"./timer.js\";\n\nexport default function(callback, delay, time) {\n var t = new Timer;\n delay = delay == null ? 0 : +delay;\n t.restart(function(elapsed) {\n t.stop();\n callback(elapsed + delay);\n }, delay, time);\n return t;\n}\n","import {dispatch} from \"d3-dispatch\";\nimport {timer, timeout} from \"d3-timer\";\n\nvar emptyOn = dispatch(\"start\", \"end\", \"cancel\", \"interrupt\");\nvar emptyTween = [];\n\nexport var CREATED = 0;\nexport var SCHEDULED = 1;\nexport var STARTING = 2;\nexport var STARTED = 3;\nexport var RUNNING = 4;\nexport var ENDING = 5;\nexport var ENDED = 6;\n\nexport default function(node, name, id, index, group, timing) {\n var schedules = node.__transition;\n if (!schedules) node.__transition = {};\n else if (id in schedules) return;\n create(node, id, {\n name: name,\n index: index, // For context during callback.\n group: group, // For context during callback.\n on: emptyOn,\n tween: emptyTween,\n time: timing.time,\n delay: timing.delay,\n duration: timing.duration,\n ease: timing.ease,\n timer: null,\n state: CREATED\n });\n}\n\nexport function init(node, id) {\n var schedule = get(node, id);\n if (schedule.state > CREATED) throw new Error(\"too late; already scheduled\");\n return schedule;\n}\n\nexport function set(node, id) {\n var schedule = get(node, id);\n if (schedule.state > STARTED) throw new Error(\"too late; already running\");\n return schedule;\n}\n\nexport function get(node, id) {\n var schedule = node.__transition;\n if (!schedule || !(schedule = schedule[id])) throw new Error(\"transition not found\");\n return schedule;\n}\n\nfunction create(node, id, self) {\n var schedules = node.__transition,\n tween;\n\n // Initialize the self timer when the transition is created.\n // Note the actual delay is not known until the first callback!\n schedules[id] = self;\n self.timer = timer(schedule, 0, self.time);\n\n function schedule(elapsed) {\n self.state = SCHEDULED;\n self.timer.restart(start, self.delay, self.time);\n\n // If the elapsed delay is less than our first sleep, start immediately.\n if (self.delay <= elapsed) start(elapsed - self.delay);\n }\n\n function start(elapsed) {\n var i, j, n, o;\n\n // If the state is not SCHEDULED, then we previously errored on start.\n if (self.state !== SCHEDULED) return stop();\n\n for (i in schedules) {\n o = schedules[i];\n if (o.name !== self.name) continue;\n\n // While this element already has a starting transition during this frame,\n // defer starting an interrupting transition until that transition has a\n // chance to tick (and possibly end); see d3/d3-transition#54!\n if (o.state === STARTED) return timeout(start);\n\n // Interrupt the active transition, if any.\n if (o.state === RUNNING) {\n o.state = ENDED;\n o.timer.stop();\n o.on.call(\"interrupt\", node, node.__data__, o.index, o.group);\n delete schedules[i];\n }\n\n // Cancel any pre-empted transitions.\n else if (+i < id) {\n o.state = ENDED;\n o.timer.stop();\n o.on.call(\"cancel\", node, node.__data__, o.index, o.group);\n delete schedules[i];\n }\n }\n\n // Defer the first tick to end of the current frame; see d3/d3#1576.\n // Note the transition may be canceled after start and before the first tick!\n // Note this must be scheduled before the start event; see d3/d3-transition#16!\n // Assuming this is successful, subsequent callbacks go straight to tick.\n timeout(function() {\n if (self.state === STARTED) {\n self.state = RUNNING;\n self.timer.restart(tick, self.delay, self.time);\n tick(elapsed);\n }\n });\n\n // Dispatch the start event.\n // Note this must be done before the tween are initialized.\n self.state = STARTING;\n self.on.call(\"start\", node, node.__data__, self.index, self.group);\n if (self.state !== STARTING) return; // interrupted\n self.state = STARTED;\n\n // Initialize the tween, deleting null tween.\n tween = new Array(n = self.tween.length);\n for (i = 0, j = -1; i < n; ++i) {\n if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) {\n tween[++j] = o;\n }\n }\n tween.length = j + 1;\n }\n\n function tick(elapsed) {\n var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1),\n i = -1,\n n = tween.length;\n\n while (++i < n) {\n tween[i].call(node, t);\n }\n\n // Dispatch the end event.\n if (self.state === ENDING) {\n self.on.call(\"end\", node, node.__data__, self.index, self.group);\n stop();\n }\n }\n\n function stop() {\n self.state = ENDED;\n self.timer.stop();\n delete schedules[id];\n for (var i in schedules) return; // eslint-disable-line no-unused-vars\n delete node.__transition;\n }\n}\n","import {STARTING, ENDING, ENDED} from \"./transition/schedule.js\";\n\nexport default function(node, name) {\n var schedules = node.__transition,\n schedule,\n active,\n empty = true,\n i;\n\n if (!schedules) return;\n\n name = name == null ? null : name + \"\";\n\n for (i in schedules) {\n if ((schedule = schedules[i]).name !== name) { empty = false; continue; }\n active = schedule.state > STARTING && schedule.state < ENDING;\n schedule.state = ENDED;\n schedule.timer.stop();\n schedule.on.call(active ? \"interrupt\" : \"cancel\", node, node.__data__, schedule.index, schedule.group);\n delete schedules[i];\n }\n\n if (empty) delete node.__transition;\n}\n","import interrupt from \"../interrupt.js\";\n\nexport default function(name) {\n return this.each(function() {\n interrupt(this, name);\n });\n}\n","export default function(constructor, factory, prototype) {\n constructor.prototype = factory.prototype = prototype;\n prototype.constructor = constructor;\n}\n\nexport function extend(parent, definition) {\n var prototype = Object.create(parent.prototype);\n for (var key in definition) prototype[key] = definition[key];\n return prototype;\n}\n","import define, {extend} from \"./define.js\";\n\nexport function Color() {}\n\nexport var darker = 0.7;\nexport var brighter = 1 / darker;\n\nvar reI = \"\\\\s*([+-]?\\\\d+)\\\\s*\",\n reN = \"\\\\s*([+-]?\\\\d*\\\\.?\\\\d+(?:[eE][+-]?\\\\d+)?)\\\\s*\",\n reP = \"\\\\s*([+-]?\\\\d*\\\\.?\\\\d+(?:[eE][+-]?\\\\d+)?)%\\\\s*\",\n reHex = /^#([0-9a-f]{3,8})$/,\n reRgbInteger = new RegExp(\"^rgb\\\\(\" + [reI, reI, reI] + \"\\\\)$\"),\n reRgbPercent = new RegExp(\"^rgb\\\\(\" + [reP, reP, reP] + \"\\\\)$\"),\n reRgbaInteger = new RegExp(\"^rgba\\\\(\" + [reI, reI, reI, reN] + \"\\\\)$\"),\n reRgbaPercent = new RegExp(\"^rgba\\\\(\" + [reP, reP, reP, reN] + \"\\\\)$\"),\n reHslPercent = new RegExp(\"^hsl\\\\(\" + [reN, reP, reP] + \"\\\\)$\"),\n reHslaPercent = new RegExp(\"^hsla\\\\(\" + [reN, reP, reP, reN] + \"\\\\)$\");\n\nvar named = {\n aliceblue: 0xf0f8ff,\n antiquewhite: 0xfaebd7,\n aqua: 0x00ffff,\n aquamarine: 0x7fffd4,\n azure: 0xf0ffff,\n beige: 0xf5f5dc,\n bisque: 0xffe4c4,\n black: 0x000000,\n blanchedalmond: 0xffebcd,\n blue: 0x0000ff,\n blueviolet: 0x8a2be2,\n brown: 0xa52a2a,\n burlywood: 0xdeb887,\n cadetblue: 0x5f9ea0,\n chartreuse: 0x7fff00,\n chocolate: 0xd2691e,\n coral: 0xff7f50,\n cornflowerblue: 0x6495ed,\n cornsilk: 0xfff8dc,\n crimson: 0xdc143c,\n cyan: 0x00ffff,\n darkblue: 0x00008b,\n darkcyan: 0x008b8b,\n darkgoldenrod: 0xb8860b,\n darkgray: 0xa9a9a9,\n darkgreen: 0x006400,\n darkgrey: 0xa9a9a9,\n darkkhaki: 0xbdb76b,\n darkmagenta: 0x8b008b,\n darkolivegreen: 0x556b2f,\n darkorange: 0xff8c00,\n darkorchid: 0x9932cc,\n darkred: 0x8b0000,\n darksalmon: 0xe9967a,\n darkseagreen: 0x8fbc8f,\n darkslateblue: 0x483d8b,\n darkslategray: 0x2f4f4f,\n darkslategrey: 0x2f4f4f,\n darkturquoise: 0x00ced1,\n darkviolet: 0x9400d3,\n deeppink: 0xff1493,\n deepskyblue: 0x00bfff,\n dimgray: 0x696969,\n dimgrey: 0x696969,\n dodgerblue: 0x1e90ff,\n firebrick: 0xb22222,\n floralwhite: 0xfffaf0,\n forestgreen: 0x228b22,\n fuchsia: 0xff00ff,\n gainsboro: 0xdcdcdc,\n ghostwhite: 0xf8f8ff,\n gold: 0xffd700,\n goldenrod: 0xdaa520,\n gray: 0x808080,\n green: 0x008000,\n greenyellow: 0xadff2f,\n grey: 0x808080,\n honeydew: 0xf0fff0,\n hotpink: 0xff69b4,\n indianred: 0xcd5c5c,\n indigo: 0x4b0082,\n ivory: 0xfffff0,\n khaki: 0xf0e68c,\n lavender: 0xe6e6fa,\n lavenderblush: 0xfff0f5,\n lawngreen: 0x7cfc00,\n lemonchiffon: 0xfffacd,\n lightblue: 0xadd8e6,\n lightcoral: 0xf08080,\n lightcyan: 0xe0ffff,\n lightgoldenrodyellow: 0xfafad2,\n lightgray: 0xd3d3d3,\n lightgreen: 0x90ee90,\n lightgrey: 0xd3d3d3,\n lightpink: 0xffb6c1,\n lightsalmon: 0xffa07a,\n lightseagreen: 0x20b2aa,\n lightskyblue: 0x87cefa,\n lightslategray: 0x778899,\n lightslategrey: 0x778899,\n lightsteelblue: 0xb0c4de,\n lightyellow: 0xffffe0,\n lime: 0x00ff00,\n limegreen: 0x32cd32,\n linen: 0xfaf0e6,\n magenta: 0xff00ff,\n maroon: 0x800000,\n mediumaquamarine: 0x66cdaa,\n mediumblue: 0x0000cd,\n mediumorchid: 0xba55d3,\n mediumpurple: 0x9370db,\n mediumseagreen: 0x3cb371,\n mediumslateblue: 0x7b68ee,\n mediumspringgreen: 0x00fa9a,\n mediumturquoise: 0x48d1cc,\n mediumvioletred: 0xc71585,\n midnightblue: 0x191970,\n mintcream: 0xf5fffa,\n mistyrose: 0xffe4e1,\n moccasin: 0xffe4b5,\n navajowhite: 0xffdead,\n navy: 0x000080,\n oldlace: 0xfdf5e6,\n olive: 0x808000,\n olivedrab: 0x6b8e23,\n orange: 0xffa500,\n orangered: 0xff4500,\n orchid: 0xda70d6,\n palegoldenrod: 0xeee8aa,\n palegreen: 0x98fb98,\n paleturquoise: 0xafeeee,\n palevioletred: 0xdb7093,\n papayawhip: 0xffefd5,\n peachpuff: 0xffdab9,\n peru: 0xcd853f,\n pink: 0xffc0cb,\n plum: 0xdda0dd,\n powderblue: 0xb0e0e6,\n purple: 0x800080,\n rebeccapurple: 0x663399,\n red: 0xff0000,\n rosybrown: 0xbc8f8f,\n royalblue: 0x4169e1,\n saddlebrown: 0x8b4513,\n salmon: 0xfa8072,\n sandybrown: 0xf4a460,\n seagreen: 0x2e8b57,\n seashell: 0xfff5ee,\n sienna: 0xa0522d,\n silver: 0xc0c0c0,\n skyblue: 0x87ceeb,\n slateblue: 0x6a5acd,\n slategray: 0x708090,\n slategrey: 0x708090,\n snow: 0xfffafa,\n springgreen: 0x00ff7f,\n steelblue: 0x4682b4,\n tan: 0xd2b48c,\n teal: 0x008080,\n thistle: 0xd8bfd8,\n tomato: 0xff6347,\n turquoise: 0x40e0d0,\n violet: 0xee82ee,\n wheat: 0xf5deb3,\n white: 0xffffff,\n whitesmoke: 0xf5f5f5,\n yellow: 0xffff00,\n yellowgreen: 0x9acd32\n};\n\ndefine(Color, color, {\n copy: function(channels) {\n return Object.assign(new this.constructor, this, channels);\n },\n displayable: function() {\n return this.rgb().displayable();\n },\n hex: color_formatHex, // Deprecated! Use color.formatHex.\n formatHex: color_formatHex,\n formatHsl: color_formatHsl,\n formatRgb: color_formatRgb,\n toString: color_formatRgb\n});\n\nfunction color_formatHex() {\n return this.rgb().formatHex();\n}\n\nfunction color_formatHsl() {\n return hslConvert(this).formatHsl();\n}\n\nfunction color_formatRgb() {\n return this.rgb().formatRgb();\n}\n\nexport default function color(format) {\n var m, l;\n format = (format + \"\").trim().toLowerCase();\n return (m = reHex.exec(format)) ? (l = m[1].length, m = parseInt(m[1], 16), l === 6 ? rgbn(m) // #ff0000\n : l === 3 ? new Rgb((m >> 8 & 0xf) | (m >> 4 & 0xf0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1) // #f00\n : l === 8 ? new Rgb(m >> 24 & 0xff, m >> 16 & 0xff, m >> 8 & 0xff, (m & 0xff) / 0xff) // #ff000000\n : l === 4 ? new Rgb((m >> 12 & 0xf) | (m >> 8 & 0xf0), (m >> 8 & 0xf) | (m >> 4 & 0xf0), (m >> 4 & 0xf) | (m & 0xf0), (((m & 0xf) << 4) | (m & 0xf)) / 0xff) // #f000\n : null) // invalid hex\n : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)\n : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)\n : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)\n : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)\n : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)\n : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)\n : named.hasOwnProperty(format) ? rgbn(named[format]) // eslint-disable-line no-prototype-builtins\n : format === \"transparent\" ? new Rgb(NaN, NaN, NaN, 0)\n : null;\n}\n\nfunction rgbn(n) {\n return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);\n}\n\nfunction rgba(r, g, b, a) {\n if (a <= 0) r = g = b = NaN;\n return new Rgb(r, g, b, a);\n}\n\nexport function rgbConvert(o) {\n if (!(o instanceof Color)) o = color(o);\n if (!o) return new Rgb;\n o = o.rgb();\n return new Rgb(o.r, o.g, o.b, o.opacity);\n}\n\nexport function rgb(r, g, b, opacity) {\n return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);\n}\n\nexport function Rgb(r, g, b, opacity) {\n this.r = +r;\n this.g = +g;\n this.b = +b;\n this.opacity = +opacity;\n}\n\ndefine(Rgb, rgb, extend(Color, {\n brighter: function(k) {\n k = k == null ? brighter : Math.pow(brighter, k);\n return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);\n },\n darker: function(k) {\n k = k == null ? darker : Math.pow(darker, k);\n return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);\n },\n rgb: function() {\n return this;\n },\n displayable: function() {\n return (-0.5 <= this.r && this.r < 255.5)\n && (-0.5 <= this.g && this.g < 255.5)\n && (-0.5 <= this.b && this.b < 255.5)\n && (0 <= this.opacity && this.opacity <= 1);\n },\n hex: rgb_formatHex, // Deprecated! Use color.formatHex.\n formatHex: rgb_formatHex,\n formatRgb: rgb_formatRgb,\n toString: rgb_formatRgb\n}));\n\nfunction rgb_formatHex() {\n return \"#\" + hex(this.r) + hex(this.g) + hex(this.b);\n}\n\nfunction rgb_formatRgb() {\n var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));\n return (a === 1 ? \"rgb(\" : \"rgba(\")\n + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + \", \"\n + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + \", \"\n + Math.max(0, Math.min(255, Math.round(this.b) || 0))\n + (a === 1 ? \")\" : \", \" + a + \")\");\n}\n\nfunction hex(value) {\n value = Math.max(0, Math.min(255, Math.round(value) || 0));\n return (value < 16 ? \"0\" : \"\") + value.toString(16);\n}\n\nfunction hsla(h, s, l, a) {\n if (a <= 0) h = s = l = NaN;\n else if (l <= 0 || l >= 1) h = s = NaN;\n else if (s <= 0) h = NaN;\n return new Hsl(h, s, l, a);\n}\n\nexport function hslConvert(o) {\n if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);\n if (!(o instanceof Color)) o = color(o);\n if (!o) return new Hsl;\n if (o instanceof Hsl) return o;\n o = o.rgb();\n var r = o.r / 255,\n g = o.g / 255,\n b = o.b / 255,\n min = Math.min(r, g, b),\n max = Math.max(r, g, b),\n h = NaN,\n s = max - min,\n l = (max + min) / 2;\n if (s) {\n if (r === max) h = (g - b) / s + (g < b) * 6;\n else if (g === max) h = (b - r) / s + 2;\n else h = (r - g) / s + 4;\n s /= l < 0.5 ? max + min : 2 - max - min;\n h *= 60;\n } else {\n s = l > 0 && l < 1 ? 0 : h;\n }\n return new Hsl(h, s, l, o.opacity);\n}\n\nexport function hsl(h, s, l, opacity) {\n return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);\n}\n\nfunction Hsl(h, s, l, opacity) {\n this.h = +h;\n this.s = +s;\n this.l = +l;\n this.opacity = +opacity;\n}\n\ndefine(Hsl, hsl, extend(Color, {\n brighter: function(k) {\n k = k == null ? brighter : Math.pow(brighter, k);\n return new Hsl(this.h, this.s, this.l * k, this.opacity);\n },\n darker: function(k) {\n k = k == null ? darker : Math.pow(darker, k);\n return new Hsl(this.h, this.s, this.l * k, this.opacity);\n },\n rgb: function() {\n var h = this.h % 360 + (this.h < 0) * 360,\n s = isNaN(h) || isNaN(this.s) ? 0 : this.s,\n l = this.l,\n m2 = l + (l < 0.5 ? l : 1 - l) * s,\n m1 = 2 * l - m2;\n return new Rgb(\n hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),\n hsl2rgb(h, m1, m2),\n hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),\n this.opacity\n );\n },\n displayable: function() {\n return (0 <= this.s && this.s <= 1 || isNaN(this.s))\n && (0 <= this.l && this.l <= 1)\n && (0 <= this.opacity && this.opacity <= 1);\n },\n formatHsl: function() {\n var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));\n return (a === 1 ? \"hsl(\" : \"hsla(\")\n + (this.h || 0) + \", \"\n + (this.s || 0) * 100 + \"%, \"\n + (this.l || 0) * 100 + \"%\"\n + (a === 1 ? \")\" : \", \" + a + \")\");\n }\n}));\n\n/* From FvD 13.37, CSS Color Module Level 3 */\nfunction hsl2rgb(h, m1, m2) {\n return (h < 60 ? m1 + (m2 - m1) * h / 60\n : h < 180 ? m2\n : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60\n : m1) * 255;\n}\n","export var deg2rad = Math.PI / 180;\nexport var rad2deg = 180 / Math.PI;\n","import define, {extend} from \"./define.js\";\nimport {Color, rgbConvert, Rgb} from \"./color.js\";\nimport {deg2rad, rad2deg} from \"./math.js\";\n\n// https://observablehq.com/@mbostock/lab-and-rgb\nvar K = 18,\n Xn = 0.96422,\n Yn = 1,\n Zn = 0.82521,\n t0 = 4 / 29,\n t1 = 6 / 29,\n t2 = 3 * t1 * t1,\n t3 = t1 * t1 * t1;\n\nfunction labConvert(o) {\n if (o instanceof Lab) return new Lab(o.l, o.a, o.b, o.opacity);\n if (o instanceof Hcl) return hcl2lab(o);\n if (!(o instanceof Rgb)) o = rgbConvert(o);\n var r = rgb2lrgb(o.r),\n g = rgb2lrgb(o.g),\n b = rgb2lrgb(o.b),\n y = xyz2lab((0.2225045 * r + 0.7168786 * g + 0.0606169 * b) / Yn), x, z;\n if (r === g && g === b) x = z = y; else {\n x = xyz2lab((0.4360747 * r + 0.3850649 * g + 0.1430804 * b) / Xn);\n z = xyz2lab((0.0139322 * r + 0.0971045 * g + 0.7141733 * b) / Zn);\n }\n return new Lab(116 * y - 16, 500 * (x - y), 200 * (y - z), o.opacity);\n}\n\nexport function gray(l, opacity) {\n return new Lab(l, 0, 0, opacity == null ? 1 : opacity);\n}\n\nexport default function lab(l, a, b, opacity) {\n return arguments.length === 1 ? labConvert(l) : new Lab(l, a, b, opacity == null ? 1 : opacity);\n}\n\nexport function Lab(l, a, b, opacity) {\n this.l = +l;\n this.a = +a;\n this.b = +b;\n this.opacity = +opacity;\n}\n\ndefine(Lab, lab, extend(Color, {\n brighter: function(k) {\n return new Lab(this.l + K * (k == null ? 1 : k), this.a, this.b, this.opacity);\n },\n darker: function(k) {\n return new Lab(this.l - K * (k == null ? 1 : k), this.a, this.b, this.opacity);\n },\n rgb: function() {\n var y = (this.l + 16) / 116,\n x = isNaN(this.a) ? y : y + this.a / 500,\n z = isNaN(this.b) ? y : y - this.b / 200;\n x = Xn * lab2xyz(x);\n y = Yn * lab2xyz(y);\n z = Zn * lab2xyz(z);\n return new Rgb(\n lrgb2rgb( 3.1338561 * x - 1.6168667 * y - 0.4906146 * z),\n lrgb2rgb(-0.9787684 * x + 1.9161415 * y + 0.0334540 * z),\n lrgb2rgb( 0.0719453 * x - 0.2289914 * y + 1.4052427 * z),\n this.opacity\n );\n }\n}));\n\nfunction xyz2lab(t) {\n return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0;\n}\n\nfunction lab2xyz(t) {\n return t > t1 ? t * t * t : t2 * (t - t0);\n}\n\nfunction lrgb2rgb(x) {\n return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055);\n}\n\nfunction rgb2lrgb(x) {\n return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);\n}\n\nfunction hclConvert(o) {\n if (o instanceof Hcl) return new Hcl(o.h, o.c, o.l, o.opacity);\n if (!(o instanceof Lab)) o = labConvert(o);\n if (o.a === 0 && o.b === 0) return new Hcl(NaN, 0 < o.l && o.l < 100 ? 0 : NaN, o.l, o.opacity);\n var h = Math.atan2(o.b, o.a) * rad2deg;\n return new Hcl(h < 0 ? h + 360 : h, Math.sqrt(o.a * o.a + o.b * o.b), o.l, o.opacity);\n}\n\nexport function lch(l, c, h, opacity) {\n return arguments.length === 1 ? hclConvert(l) : new Hcl(h, c, l, opacity == null ? 1 : opacity);\n}\n\nexport function hcl(h, c, l, opacity) {\n return arguments.length === 1 ? hclConvert(h) : new Hcl(h, c, l, opacity == null ? 1 : opacity);\n}\n\nexport function Hcl(h, c, l, opacity) {\n this.h = +h;\n this.c = +c;\n this.l = +l;\n this.opacity = +opacity;\n}\n\nfunction hcl2lab(o) {\n if (isNaN(o.h)) return new Lab(o.l, 0, 0, o.opacity);\n var h = o.h * deg2rad;\n return new Lab(o.l, Math.cos(h) * o.c, Math.sin(h) * o.c, o.opacity);\n}\n\ndefine(Hcl, hcl, extend(Color, {\n brighter: function(k) {\n return new Hcl(this.h, this.c, this.l + K * (k == null ? 1 : k), this.opacity);\n },\n darker: function(k) {\n return new Hcl(this.h, this.c, this.l - K * (k == null ? 1 : k), this.opacity);\n },\n rgb: function() {\n return hcl2lab(this).rgb();\n }\n}));\n","export default function(x) {\n return function() {\n return x;\n };\n}\n","import constant from \"./constant.js\";\n\nfunction linear(a, d) {\n return function(t) {\n return a + t * d;\n };\n}\n\nfunction exponential(a, b, y) {\n return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) {\n return Math.pow(a + t * b, y);\n };\n}\n\nexport function hue(a, b) {\n var d = b - a;\n return d ? linear(a, d > 180 || d < -180 ? d - 360 * Math.round(d / 360) : d) : constant(isNaN(a) ? b : a);\n}\n\nexport function gamma(y) {\n return (y = +y) === 1 ? nogamma : function(a, b) {\n return b - a ? exponential(a, b, y) : constant(isNaN(a) ? b : a);\n };\n}\n\nexport default function nogamma(a, b) {\n var d = b - a;\n return d ? linear(a, d) : constant(isNaN(a) ? b : a);\n}\n","import {rgb as colorRgb} from \"d3-color\";\nimport basis from \"./basis.js\";\nimport basisClosed from \"./basisClosed.js\";\nimport nogamma, {gamma} from \"./color.js\";\n\nexport default (function rgbGamma(y) {\n var color = gamma(y);\n\n function rgb(start, end) {\n var r = color((start = colorRgb(start)).r, (end = colorRgb(end)).r),\n g = color(start.g, end.g),\n b = color(start.b, end.b),\n opacity = nogamma(start.opacity, end.opacity);\n return function(t) {\n start.r = r(t);\n start.g = g(t);\n start.b = b(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n\n rgb.gamma = rgbGamma;\n\n return rgb;\n})(1);\n\nfunction rgbSpline(spline) {\n return function(colors) {\n var n = colors.length,\n r = new Array(n),\n g = new Array(n),\n b = new Array(n),\n i, color;\n for (i = 0; i < n; ++i) {\n color = colorRgb(colors[i]);\n r[i] = color.r || 0;\n g[i] = color.g || 0;\n b[i] = color.b || 0;\n }\n r = spline(r);\n g = spline(g);\n b = spline(b);\n color.opacity = 1;\n return function(t) {\n color.r = r(t);\n color.g = g(t);\n color.b = b(t);\n return color + \"\";\n };\n };\n}\n\nexport var rgbBasis = rgbSpline(basis);\nexport var rgbBasisClosed = rgbSpline(basisClosed);\n","export default function(a, b) {\n if (!b) b = [];\n var n = a ? Math.min(b.length, a.length) : 0,\n c = b.slice(),\n i;\n return function(t) {\n for (i = 0; i < n; ++i) c[i] = a[i] * (1 - t) + b[i] * t;\n return c;\n };\n}\n\nexport function isNumberArray(x) {\n return ArrayBuffer.isView(x) && !(x instanceof DataView);\n}\n","import value from \"./value.js\";\nimport numberArray, {isNumberArray} from \"./numberArray.js\";\n\nexport default function(a, b) {\n return (isNumberArray(b) ? numberArray : genericArray)(a, b);\n}\n\nexport function genericArray(a, b) {\n var nb = b ? b.length : 0,\n na = a ? Math.min(nb, a.length) : 0,\n x = new Array(na),\n c = new Array(nb),\n i;\n\n for (i = 0; i < na; ++i) x[i] = value(a[i], b[i]);\n for (; i < nb; ++i) c[i] = b[i];\n\n return function(t) {\n for (i = 0; i < na; ++i) c[i] = x[i](t);\n return c;\n };\n}\n","export default function(a, b) {\n var d = new Date;\n return a = +a, b = +b, function(t) {\n return d.setTime(a * (1 - t) + b * t), d;\n };\n}\n","export default function(a, b) {\n return a = +a, b = +b, function(t) {\n return a * (1 - t) + b * t;\n };\n}\n","import value from \"./value.js\";\n\nexport default function(a, b) {\n var i = {},\n c = {},\n k;\n\n if (a === null || typeof a !== \"object\") a = {};\n if (b === null || typeof b !== \"object\") b = {};\n\n for (k in b) {\n if (k in a) {\n i[k] = value(a[k], b[k]);\n } else {\n c[k] = b[k];\n }\n }\n\n return function(t) {\n for (k in i) c[k] = i[k](t);\n return c;\n };\n}\n","import number from \"./number.js\";\n\nvar reA = /[-+]?(?:\\d+\\.?\\d*|\\.?\\d+)(?:[eE][-+]?\\d+)?/g,\n reB = new RegExp(reA.source, \"g\");\n\nfunction zero(b) {\n return function() {\n return b;\n };\n}\n\nfunction one(b) {\n return function(t) {\n return b(t) + \"\";\n };\n}\n\nexport default function(a, b) {\n var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b\n am, // current match in a\n bm, // current match in b\n bs, // string preceding current number in b, if any\n i = -1, // index in s\n s = [], // string constants and placeholders\n q = []; // number interpolators\n\n // Coerce inputs to strings.\n a = a + \"\", b = b + \"\";\n\n // Interpolate pairs of numbers in a & b.\n while ((am = reA.exec(a))\n && (bm = reB.exec(b))) {\n if ((bs = bm.index) > bi) { // a string precedes the next number in b\n bs = b.slice(bi, bs);\n if (s[i]) s[i] += bs; // coalesce with previous string\n else s[++i] = bs;\n }\n if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match\n if (s[i]) s[i] += bm; // coalesce with previous string\n else s[++i] = bm;\n } else { // interpolate non-matching numbers\n s[++i] = null;\n q.push({i: i, x: number(am, bm)});\n }\n bi = reB.lastIndex;\n }\n\n // Add remains of b.\n if (bi < b.length) {\n bs = b.slice(bi);\n if (s[i]) s[i] += bs; // coalesce with previous string\n else s[++i] = bs;\n }\n\n // Special optimization for only a single match.\n // Otherwise, interpolate each of the numbers and rejoin the string.\n return s.length < 2 ? (q[0]\n ? one(q[0].x)\n : zero(b))\n : (b = q.length, function(t) {\n for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);\n return s.join(\"\");\n });\n}\n","import {color} from \"d3-color\";\nimport rgb from \"./rgb.js\";\nimport {genericArray} from \"./array.js\";\nimport date from \"./date.js\";\nimport number from \"./number.js\";\nimport object from \"./object.js\";\nimport string from \"./string.js\";\nimport constant from \"./constant.js\";\nimport numberArray, {isNumberArray} from \"./numberArray.js\";\n\nexport default function(a, b) {\n var t = typeof b, c;\n return b == null || t === \"boolean\" ? constant(b)\n : (t === \"number\" ? number\n : t === \"string\" ? ((c = color(b)) ? (b = c, rgb) : string)\n : b instanceof color ? rgb\n : b instanceof Date ? date\n : isNumberArray(b) ? numberArray\n : Array.isArray(b) ? genericArray\n : typeof b.valueOf !== \"function\" && typeof b.toString !== \"function\" || isNaN(b) ? object\n : number)(a, b);\n}\n","export default function(a, b) {\n return a = +a, b = +b, function(t) {\n return Math.round(a * (1 - t) + b * t);\n };\n}\n","var degrees = 180 / Math.PI;\n\nexport var identity = {\n translateX: 0,\n translateY: 0,\n rotate: 0,\n skewX: 0,\n scaleX: 1,\n scaleY: 1\n};\n\nexport default function(a, b, c, d, e, f) {\n var scaleX, scaleY, skewX;\n if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX;\n if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX;\n if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY;\n if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX;\n return {\n translateX: e,\n translateY: f,\n rotate: Math.atan2(b, a) * degrees,\n skewX: Math.atan(skewX) * degrees,\n scaleX: scaleX,\n scaleY: scaleY\n };\n}\n","import decompose, {identity} from \"./decompose.js\";\n\nvar cssNode,\n cssRoot,\n cssView,\n svgNode;\n\nexport function parseCss(value) {\n if (value === \"none\") return identity;\n if (!cssNode) cssNode = document.createElement(\"DIV\"), cssRoot = document.documentElement, cssView = document.defaultView;\n cssNode.style.transform = value;\n value = cssView.getComputedStyle(cssRoot.appendChild(cssNode), null).getPropertyValue(\"transform\");\n cssRoot.removeChild(cssNode);\n value = value.slice(7, -1).split(\",\");\n return decompose(+value[0], +value[1], +value[2], +value[3], +value[4], +value[5]);\n}\n\nexport function parseSvg(value) {\n if (value == null) return identity;\n if (!svgNode) svgNode = document.createElementNS(\"http://www.w3.org/2000/svg\", \"g\");\n svgNode.setAttribute(\"transform\", value);\n if (!(value = svgNode.transform.baseVal.consolidate())) return identity;\n value = value.matrix;\n return decompose(value.a, value.b, value.c, value.d, value.e, value.f);\n}\n","import number from \"../number.js\";\nimport {parseCss, parseSvg} from \"./parse.js\";\n\nfunction interpolateTransform(parse, pxComma, pxParen, degParen) {\n\n function pop(s) {\n return s.length ? s.pop() + \" \" : \"\";\n }\n\n function translate(xa, ya, xb, yb, s, q) {\n if (xa !== xb || ya !== yb) {\n var i = s.push(\"translate(\", null, pxComma, null, pxParen);\n q.push({i: i - 4, x: number(xa, xb)}, {i: i - 2, x: number(ya, yb)});\n } else if (xb || yb) {\n s.push(\"translate(\" + xb + pxComma + yb + pxParen);\n }\n }\n\n function rotate(a, b, s, q) {\n if (a !== b) {\n if (a - b > 180) b += 360; else if (b - a > 180) a += 360; // shortest path\n q.push({i: s.push(pop(s) + \"rotate(\", null, degParen) - 2, x: number(a, b)});\n } else if (b) {\n s.push(pop(s) + \"rotate(\" + b + degParen);\n }\n }\n\n function skewX(a, b, s, q) {\n if (a !== b) {\n q.push({i: s.push(pop(s) + \"skewX(\", null, degParen) - 2, x: number(a, b)});\n } else if (b) {\n s.push(pop(s) + \"skewX(\" + b + degParen);\n }\n }\n\n function scale(xa, ya, xb, yb, s, q) {\n if (xa !== xb || ya !== yb) {\n var i = s.push(pop(s) + \"scale(\", null, \",\", null, \")\");\n q.push({i: i - 4, x: number(xa, xb)}, {i: i - 2, x: number(ya, yb)});\n } else if (xb !== 1 || yb !== 1) {\n s.push(pop(s) + \"scale(\" + xb + \",\" + yb + \")\");\n }\n }\n\n return function(a, b) {\n var s = [], // string constants and placeholders\n q = []; // number interpolators\n a = parse(a), b = parse(b);\n translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q);\n rotate(a.rotate, b.rotate, s, q);\n skewX(a.skewX, b.skewX, s, q);\n scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q);\n a = b = null; // gc\n return function(t) {\n var i = -1, n = q.length, o;\n while (++i < n) s[(o = q[i]).i] = o.x(t);\n return s.join(\"\");\n };\n };\n}\n\nexport var interpolateTransformCss = interpolateTransform(parseCss, \"px, \", \"px)\", \"deg)\");\nexport var interpolateTransformSvg = interpolateTransform(parseSvg, \", \", \")\", \")\");\n","import {get, set} from \"./schedule.js\";\n\nfunction tweenRemove(id, name) {\n var tween0, tween1;\n return function() {\n var schedule = set(this, id),\n tween = schedule.tween;\n\n // If this node shared tween with the previous node,\n // just assign the updated shared tween and we’re done!\n // Otherwise, copy-on-write.\n if (tween !== tween0) {\n tween1 = tween0 = tween;\n for (var i = 0, n = tween1.length; i < n; ++i) {\n if (tween1[i].name === name) {\n tween1 = tween1.slice();\n tween1.splice(i, 1);\n break;\n }\n }\n }\n\n schedule.tween = tween1;\n };\n}\n\nfunction tweenFunction(id, name, value) {\n var tween0, tween1;\n if (typeof value !== \"function\") throw new Error;\n return function() {\n var schedule = set(this, id),\n tween = schedule.tween;\n\n // If this node shared tween with the previous node,\n // just assign the updated shared tween and we’re done!\n // Otherwise, copy-on-write.\n if (tween !== tween0) {\n tween1 = (tween0 = tween).slice();\n for (var t = {name: name, value: value}, i = 0, n = tween1.length; i < n; ++i) {\n if (tween1[i].name === name) {\n tween1[i] = t;\n break;\n }\n }\n if (i === n) tween1.push(t);\n }\n\n schedule.tween = tween1;\n };\n}\n\nexport default function(name, value) {\n var id = this._id;\n\n name += \"\";\n\n if (arguments.length < 2) {\n var tween = get(this.node(), id).tween;\n for (var i = 0, n = tween.length, t; i < n; ++i) {\n if ((t = tween[i]).name === name) {\n return t.value;\n }\n }\n return null;\n }\n\n return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value));\n}\n\nexport function tweenValue(transition, name, value) {\n var id = transition._id;\n\n transition.each(function() {\n var schedule = set(this, id);\n (schedule.value || (schedule.value = {}))[name] = value.apply(this, arguments);\n });\n\n return function(node) {\n return get(node, id).value[name];\n };\n}\n","import {color} from \"d3-color\";\nimport {interpolateNumber, interpolateRgb, interpolateString} from \"d3-interpolate\";\n\nexport default function(a, b) {\n var c;\n return (typeof b === \"number\" ? interpolateNumber\n : b instanceof color ? interpolateRgb\n : (c = color(b)) ? (b = c, interpolateRgb)\n : interpolateString)(a, b);\n}\n","import {interpolateTransformSvg as interpolateTransform} from \"d3-interpolate\";\nimport {namespace} from \"d3-selection\";\nimport {tweenValue} from \"./tween.js\";\nimport interpolate from \"./interpolate.js\";\n\nfunction attrRemove(name) {\n return function() {\n this.removeAttribute(name);\n };\n}\n\nfunction attrRemoveNS(fullname) {\n return function() {\n this.removeAttributeNS(fullname.space, fullname.local);\n };\n}\n\nfunction attrConstant(name, interpolate, value1) {\n var string00,\n string1 = value1 + \"\",\n interpolate0;\n return function() {\n var string0 = this.getAttribute(name);\n return string0 === string1 ? null\n : string0 === string00 ? interpolate0\n : interpolate0 = interpolate(string00 = string0, value1);\n };\n}\n\nfunction attrConstantNS(fullname, interpolate, value1) {\n var string00,\n string1 = value1 + \"\",\n interpolate0;\n return function() {\n var string0 = this.getAttributeNS(fullname.space, fullname.local);\n return string0 === string1 ? null\n : string0 === string00 ? interpolate0\n : interpolate0 = interpolate(string00 = string0, value1);\n };\n}\n\nfunction attrFunction(name, interpolate, value) {\n var string00,\n string10,\n interpolate0;\n return function() {\n var string0, value1 = value(this), string1;\n if (value1 == null) return void this.removeAttribute(name);\n string0 = this.getAttribute(name);\n string1 = value1 + \"\";\n return string0 === string1 ? null\n : string0 === string00 && string1 === string10 ? interpolate0\n : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));\n };\n}\n\nfunction attrFunctionNS(fullname, interpolate, value) {\n var string00,\n string10,\n interpolate0;\n return function() {\n var string0, value1 = value(this), string1;\n if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local);\n string0 = this.getAttributeNS(fullname.space, fullname.local);\n string1 = value1 + \"\";\n return string0 === string1 ? null\n : string0 === string00 && string1 === string10 ? interpolate0\n : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));\n };\n}\n\nexport default function(name, value) {\n var fullname = namespace(name), i = fullname === \"transform\" ? interpolateTransform : interpolate;\n return this.attrTween(name, typeof value === \"function\"\n ? (fullname.local ? attrFunctionNS : attrFunction)(fullname, i, tweenValue(this, \"attr.\" + name, value))\n : value == null ? (fullname.local ? attrRemoveNS : attrRemove)(fullname)\n : (fullname.local ? attrConstantNS : attrConstant)(fullname, i, value));\n}\n","import {namespace} from \"d3-selection\";\n\nfunction attrInterpolate(name, i) {\n return function(t) {\n this.setAttribute(name, i.call(this, t));\n };\n}\n\nfunction attrInterpolateNS(fullname, i) {\n return function(t) {\n this.setAttributeNS(fullname.space, fullname.local, i.call(this, t));\n };\n}\n\nfunction attrTweenNS(fullname, value) {\n var t0, i0;\n function tween() {\n var i = value.apply(this, arguments);\n if (i !== i0) t0 = (i0 = i) && attrInterpolateNS(fullname, i);\n return t0;\n }\n tween._value = value;\n return tween;\n}\n\nfunction attrTween(name, value) {\n var t0, i0;\n function tween() {\n var i = value.apply(this, arguments);\n if (i !== i0) t0 = (i0 = i) && attrInterpolate(name, i);\n return t0;\n }\n tween._value = value;\n return tween;\n}\n\nexport default function(name, value) {\n var key = \"attr.\" + name;\n if (arguments.length < 2) return (key = this.tween(key)) && key._value;\n if (value == null) return this.tween(key, null);\n if (typeof value !== \"function\") throw new Error;\n var fullname = namespace(name);\n return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value));\n}\n","import {get, init} from \"./schedule.js\";\n\nfunction delayFunction(id, value) {\n return function() {\n init(this, id).delay = +value.apply(this, arguments);\n };\n}\n\nfunction delayConstant(id, value) {\n return value = +value, function() {\n init(this, id).delay = value;\n };\n}\n\nexport default function(value) {\n var id = this._id;\n\n return arguments.length\n ? this.each((typeof value === \"function\"\n ? delayFunction\n : delayConstant)(id, value))\n : get(this.node(), id).delay;\n}\n","import {get, set} from \"./schedule.js\";\n\nfunction durationFunction(id, value) {\n return function() {\n set(this, id).duration = +value.apply(this, arguments);\n };\n}\n\nfunction durationConstant(id, value) {\n return value = +value, function() {\n set(this, id).duration = value;\n };\n}\n\nexport default function(value) {\n var id = this._id;\n\n return arguments.length\n ? this.each((typeof value === \"function\"\n ? durationFunction\n : durationConstant)(id, value))\n : get(this.node(), id).duration;\n}\n","import {get, set} from \"./schedule.js\";\n\nfunction easeConstant(id, value) {\n if (typeof value !== \"function\") throw new Error;\n return function() {\n set(this, id).ease = value;\n };\n}\n\nexport default function(value) {\n var id = this._id;\n\n return arguments.length\n ? this.each(easeConstant(id, value))\n : get(this.node(), id).ease;\n}\n","import {matcher} from \"d3-selection\";\nimport {Transition} from \"./index.js\";\n\nexport default function(match) {\n if (typeof match !== \"function\") match = matcher(match);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {\n if ((node = group[i]) && match.call(node, node.__data__, i, group)) {\n subgroup.push(node);\n }\n }\n }\n\n return new Transition(subgroups, this._parents, this._name, this._id);\n}\n","import {Transition} from \"./index.js\";\n\nexport default function(transition) {\n if (transition._id !== this._id) throw new Error;\n\n for (var groups0 = this._groups, groups1 = transition._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {\n for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {\n if (node = group0[i] || group1[i]) {\n merge[i] = node;\n }\n }\n }\n\n for (; j < m0; ++j) {\n merges[j] = groups0[j];\n }\n\n return new Transition(merges, this._parents, this._name, this._id);\n}\n","import {get, set, init} from \"./schedule.js\";\n\nfunction start(name) {\n return (name + \"\").trim().split(/^|\\s+/).every(function(t) {\n var i = t.indexOf(\".\");\n if (i >= 0) t = t.slice(0, i);\n return !t || t === \"start\";\n });\n}\n\nfunction onFunction(id, name, listener) {\n var on0, on1, sit = start(name) ? init : set;\n return function() {\n var schedule = sit(this, id),\n on = schedule.on;\n\n // If this node shared a dispatch with the previous node,\n // just assign the updated shared dispatch and we’re done!\n // Otherwise, copy-on-write.\n if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener);\n\n schedule.on = on1;\n };\n}\n\nexport default function(name, listener) {\n var id = this._id;\n\n return arguments.length < 2\n ? get(this.node(), id).on.on(name)\n : this.each(onFunction(id, name, listener));\n}\n","function removeFunction(id) {\n return function() {\n var parent = this.parentNode;\n for (var i in this.__transition) if (+i !== id) return;\n if (parent) parent.removeChild(this);\n };\n}\n\nexport default function() {\n return this.on(\"end.remove\", removeFunction(this._id));\n}\n","import {selector} from \"d3-selection\";\nimport {Transition} from \"./index.js\";\nimport schedule, {get} from \"./schedule.js\";\n\nexport default function(select) {\n var name = this._name,\n id = this._id;\n\n if (typeof select !== \"function\") select = selector(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {\n if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {\n if (\"__data__\" in node) subnode.__data__ = node.__data__;\n subgroup[i] = subnode;\n schedule(subgroup[i], name, id, i, subgroup, get(node, id));\n }\n }\n }\n\n return new Transition(subgroups, this._parents, name, id);\n}\n","import {selectorAll} from \"d3-selection\";\nimport {Transition} from \"./index.js\";\nimport schedule, {get} from \"./schedule.js\";\n\nexport default function(select) {\n var name = this._name,\n id = this._id;\n\n if (typeof select !== \"function\") select = selectorAll(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n for (var children = select.call(node, node.__data__, i, group), child, inherit = get(node, id), k = 0, l = children.length; k < l; ++k) {\n if (child = children[k]) {\n schedule(child, name, id, k, children, inherit);\n }\n }\n subgroups.push(children);\n parents.push(node);\n }\n }\n }\n\n return new Transition(subgroups, parents, name, id);\n}\n","import {selection} from \"d3-selection\";\n\nvar Selection = selection.prototype.constructor;\n\nexport default function() {\n return new Selection(this._groups, this._parents);\n}\n","import {interpolateTransformCss as interpolateTransform} from \"d3-interpolate\";\nimport {style} from \"d3-selection\";\nimport {set} from \"./schedule.js\";\nimport {tweenValue} from \"./tween.js\";\nimport interpolate from \"./interpolate.js\";\n\nfunction styleNull(name, interpolate) {\n var string00,\n string10,\n interpolate0;\n return function() {\n var string0 = style(this, name),\n string1 = (this.style.removeProperty(name), style(this, name));\n return string0 === string1 ? null\n : string0 === string00 && string1 === string10 ? interpolate0\n : interpolate0 = interpolate(string00 = string0, string10 = string1);\n };\n}\n\nfunction styleRemove(name) {\n return function() {\n this.style.removeProperty(name);\n };\n}\n\nfunction styleConstant(name, interpolate, value1) {\n var string00,\n string1 = value1 + \"\",\n interpolate0;\n return function() {\n var string0 = style(this, name);\n return string0 === string1 ? null\n : string0 === string00 ? interpolate0\n : interpolate0 = interpolate(string00 = string0, value1);\n };\n}\n\nfunction styleFunction(name, interpolate, value) {\n var string00,\n string10,\n interpolate0;\n return function() {\n var string0 = style(this, name),\n value1 = value(this),\n string1 = value1 + \"\";\n if (value1 == null) string1 = value1 = (this.style.removeProperty(name), style(this, name));\n return string0 === string1 ? null\n : string0 === string00 && string1 === string10 ? interpolate0\n : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));\n };\n}\n\nfunction styleMaybeRemove(id, name) {\n var on0, on1, listener0, key = \"style.\" + name, event = \"end.\" + key, remove;\n return function() {\n var schedule = set(this, id),\n on = schedule.on,\n listener = schedule.value[key] == null ? remove || (remove = styleRemove(name)) : undefined;\n\n // If this node shared a dispatch with the previous node,\n // just assign the updated shared dispatch and we’re done!\n // Otherwise, copy-on-write.\n if (on !== on0 || listener0 !== listener) (on1 = (on0 = on).copy()).on(event, listener0 = listener);\n\n schedule.on = on1;\n };\n}\n\nexport default function(name, value, priority) {\n var i = (name += \"\") === \"transform\" ? interpolateTransform : interpolate;\n return value == null ? this\n .styleTween(name, styleNull(name, i))\n .on(\"end.style.\" + name, styleRemove(name))\n : typeof value === \"function\" ? this\n .styleTween(name, styleFunction(name, i, tweenValue(this, \"style.\" + name, value)))\n .each(styleMaybeRemove(this._id, name))\n : this\n .styleTween(name, styleConstant(name, i, value), priority)\n .on(\"end.style.\" + name, null);\n}\n","function styleInterpolate(name, i, priority) {\n return function(t) {\n this.style.setProperty(name, i.call(this, t), priority);\n };\n}\n\nfunction styleTween(name, value, priority) {\n var t, i0;\n function tween() {\n var i = value.apply(this, arguments);\n if (i !== i0) t = (i0 = i) && styleInterpolate(name, i, priority);\n return t;\n }\n tween._value = value;\n return tween;\n}\n\nexport default function(name, value, priority) {\n var key = \"style.\" + (name += \"\");\n if (arguments.length < 2) return (key = this.tween(key)) && key._value;\n if (value == null) return this.tween(key, null);\n if (typeof value !== \"function\") throw new Error;\n return this.tween(key, styleTween(name, value, priority == null ? \"\" : priority));\n}\n","import {tweenValue} from \"./tween.js\";\n\nfunction textConstant(value) {\n return function() {\n this.textContent = value;\n };\n}\n\nfunction textFunction(value) {\n return function() {\n var value1 = value(this);\n this.textContent = value1 == null ? \"\" : value1;\n };\n}\n\nexport default function(value) {\n return this.tween(\"text\", typeof value === \"function\"\n ? textFunction(tweenValue(this, \"text\", value))\n : textConstant(value == null ? \"\" : value + \"\"));\n}\n","function textInterpolate(i) {\n return function(t) {\n this.textContent = i.call(this, t);\n };\n}\n\nfunction textTween(value) {\n var t0, i0;\n function tween() {\n var i = value.apply(this, arguments);\n if (i !== i0) t0 = (i0 = i) && textInterpolate(i);\n return t0;\n }\n tween._value = value;\n return tween;\n}\n\nexport default function(value) {\n var key = \"text\";\n if (arguments.length < 1) return (key = this.tween(key)) && key._value;\n if (value == null) return this.tween(key, null);\n if (typeof value !== \"function\") throw new Error;\n return this.tween(key, textTween(value));\n}\n","import {Transition, newId} from \"./index.js\";\nimport schedule, {get} from \"./schedule.js\";\n\nexport default function() {\n var name = this._name,\n id0 = this._id,\n id1 = newId();\n\n for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n var inherit = get(node, id0);\n schedule(node, name, id1, i, group, {\n time: inherit.time + inherit.delay + inherit.duration,\n delay: 0,\n duration: inherit.duration,\n ease: inherit.ease\n });\n }\n }\n }\n\n return new Transition(groups, this._parents, name, id1);\n}\n","import {set} from \"./schedule.js\";\n\nexport default function() {\n var on0, on1, that = this, id = that._id, size = that.size();\n return new Promise(function(resolve, reject) {\n var cancel = {value: reject},\n end = {value: function() { if (--size === 0) resolve(); }};\n\n that.each(function() {\n var schedule = set(this, id),\n on = schedule.on;\n\n // If this node shared a dispatch with the previous node,\n // just assign the updated shared dispatch and we’re done!\n // Otherwise, copy-on-write.\n if (on !== on0) {\n on1 = (on0 = on).copy();\n on1._.cancel.push(cancel);\n on1._.interrupt.push(cancel);\n on1._.end.push(end);\n }\n\n schedule.on = on1;\n });\n });\n}\n","import {selection} from \"d3-selection\";\nimport transition_attr from \"./attr.js\";\nimport transition_attrTween from \"./attrTween.js\";\nimport transition_delay from \"./delay.js\";\nimport transition_duration from \"./duration.js\";\nimport transition_ease from \"./ease.js\";\nimport transition_filter from \"./filter.js\";\nimport transition_merge from \"./merge.js\";\nimport transition_on from \"./on.js\";\nimport transition_remove from \"./remove.js\";\nimport transition_select from \"./select.js\";\nimport transition_selectAll from \"./selectAll.js\";\nimport transition_selection from \"./selection.js\";\nimport transition_style from \"./style.js\";\nimport transition_styleTween from \"./styleTween.js\";\nimport transition_text from \"./text.js\";\nimport transition_textTween from \"./textTween.js\";\nimport transition_transition from \"./transition.js\";\nimport transition_tween from \"./tween.js\";\nimport transition_end from \"./end.js\";\n\nvar id = 0;\n\nexport function Transition(groups, parents, name, id) {\n this._groups = groups;\n this._parents = parents;\n this._name = name;\n this._id = id;\n}\n\nexport default function transition(name) {\n return selection().transition(name);\n}\n\nexport function newId() {\n return ++id;\n}\n\nvar selection_prototype = selection.prototype;\n\nTransition.prototype = transition.prototype = {\n constructor: Transition,\n select: transition_select,\n selectAll: transition_selectAll,\n filter: transition_filter,\n merge: transition_merge,\n selection: transition_selection,\n transition: transition_transition,\n call: selection_prototype.call,\n nodes: selection_prototype.nodes,\n node: selection_prototype.node,\n size: selection_prototype.size,\n empty: selection_prototype.empty,\n each: selection_prototype.each,\n on: transition_on,\n attr: transition_attr,\n attrTween: transition_attrTween,\n style: transition_style,\n styleTween: transition_styleTween,\n text: transition_text,\n textTween: transition_textTween,\n remove: transition_remove,\n tween: transition_tween,\n delay: transition_delay,\n duration: transition_duration,\n ease: transition_ease,\n end: transition_end\n};\n","export function cubicIn(t) {\n return t * t * t;\n}\n\nexport function cubicOut(t) {\n return --t * t * t + 1;\n}\n\nexport function cubicInOut(t) {\n return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2;\n}\n","import {Transition, newId} from \"../transition/index.js\";\nimport schedule from \"../transition/schedule.js\";\nimport {easeCubicInOut} from \"d3-ease\";\nimport {now} from \"d3-timer\";\n\nvar defaultTiming = {\n time: null, // Set on use.\n delay: 0,\n duration: 250,\n ease: easeCubicInOut\n};\n\nfunction inherit(node, id) {\n var timing;\n while (!(timing = node.__transition) || !(timing = timing[id])) {\n if (!(node = node.parentNode)) {\n return defaultTiming.time = now(), defaultTiming;\n }\n }\n return timing;\n}\n\nexport default function(name) {\n var id,\n timing;\n\n if (name instanceof Transition) {\n id = name._id, name = name._name;\n } else {\n id = newId(), (timing = defaultTiming).time = now(), name = name == null ? null : name + \"\";\n }\n\n for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n schedule(node, name, id, i, group, timing || inherit(node, id));\n }\n }\n }\n\n return new Transition(groups, this._parents, name, id);\n}\n","import {selection} from \"d3-selection\";\nimport selection_interrupt from \"./interrupt.js\";\nimport selection_transition from \"./transition.js\";\n\nselection.prototype.interrupt = selection_interrupt;\nselection.prototype.transition = selection_transition;\n","// Computes the decimal coefficient and exponent of the specified number x with\n// significant digits p, where x is positive and p is in [1, 21] or undefined.\n// For example, formatDecimal(1.23) returns [\"123\", 0].\nexport default function(x, p) {\n if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf(\"e\")) < 0) return null; // NaN, ±Infinity\n var i, coefficient = x.slice(0, i);\n\n // The string returned by toExponential either has the form \\d\\.\\d+e[-+]\\d+\n // (e.g., 1.2e+3) or the form \\de[-+]\\d+ (e.g., 1e+3).\n return [\n coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient,\n +x.slice(i + 1)\n ];\n}\n","import formatDecimal from \"./formatDecimal.js\";\n\nexport default function(x) {\n return x = formatDecimal(Math.abs(x)), x ? x[1] : NaN;\n}\n","export default function(grouping, thousands) {\n return function(value, width) {\n var i = value.length,\n t = [],\n j = 0,\n g = grouping[0],\n length = 0;\n\n while (i > 0 && g > 0) {\n if (length + g + 1 > width) g = Math.max(1, width - length);\n t.push(value.substring(i -= g, i + g));\n if ((length += g + 1) > width) break;\n g = grouping[j = (j + 1) % grouping.length];\n }\n\n return t.reverse().join(thousands);\n };\n}\n","export default function(numerals) {\n return function(value) {\n return value.replace(/[0-9]/g, function(i) {\n return numerals[+i];\n });\n };\n}\n","// [[fill]align][sign][symbol][0][width][,][.precision][~][type]\nvar re = /^(?:(.)?([<>=^]))?([+\\-( ])?([$#])?(0)?(\\d+)?(,)?(\\.\\d+)?(~)?([a-z%])?$/i;\n\nexport default function formatSpecifier(specifier) {\n if (!(match = re.exec(specifier))) throw new Error(\"invalid format: \" + specifier);\n var match;\n return new FormatSpecifier({\n fill: match[1],\n align: match[2],\n sign: match[3],\n symbol: match[4],\n zero: match[5],\n width: match[6],\n comma: match[7],\n precision: match[8] && match[8].slice(1),\n trim: match[9],\n type: match[10]\n });\n}\n\nformatSpecifier.prototype = FormatSpecifier.prototype; // instanceof\n\nexport function FormatSpecifier(specifier) {\n this.fill = specifier.fill === undefined ? \" \" : specifier.fill + \"\";\n this.align = specifier.align === undefined ? \">\" : specifier.align + \"\";\n this.sign = specifier.sign === undefined ? \"-\" : specifier.sign + \"\";\n this.symbol = specifier.symbol === undefined ? \"\" : specifier.symbol + \"\";\n this.zero = !!specifier.zero;\n this.width = specifier.width === undefined ? undefined : +specifier.width;\n this.comma = !!specifier.comma;\n this.precision = specifier.precision === undefined ? undefined : +specifier.precision;\n this.trim = !!specifier.trim;\n this.type = specifier.type === undefined ? \"\" : specifier.type + \"\";\n}\n\nFormatSpecifier.prototype.toString = function() {\n return this.fill\n + this.align\n + this.sign\n + this.symbol\n + (this.zero ? \"0\" : \"\")\n + (this.width === undefined ? \"\" : Math.max(1, this.width | 0))\n + (this.comma ? \",\" : \"\")\n + (this.precision === undefined ? \"\" : \".\" + Math.max(0, this.precision | 0))\n + (this.trim ? \"~\" : \"\")\n + this.type;\n};\n","// Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.\nexport default function(s) {\n out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {\n switch (s[i]) {\n case \".\": i0 = i1 = i; break;\n case \"0\": if (i0 === 0) i0 = i; i1 = i; break;\n default: if (!+s[i]) break out; if (i0 > 0) i0 = 0; break;\n }\n }\n return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;\n}\n","import formatDecimal from \"./formatDecimal.js\";\n\nexport var prefixExponent;\n\nexport default function(x, p) {\n var d = formatDecimal(x, p);\n if (!d) return x + \"\";\n var coefficient = d[0],\n exponent = d[1],\n i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,\n n = coefficient.length;\n return i === n ? coefficient\n : i > n ? coefficient + new Array(i - n + 1).join(\"0\")\n : i > 0 ? coefficient.slice(0, i) + \".\" + coefficient.slice(i)\n : \"0.\" + new Array(1 - i).join(\"0\") + formatDecimal(x, Math.max(0, p + i - 1))[0]; // less than 1y!\n}\n","import formatDecimal from \"./formatDecimal.js\";\n\nexport default function(x, p) {\n var d = formatDecimal(x, p);\n if (!d) return x + \"\";\n var coefficient = d[0],\n exponent = d[1];\n return exponent < 0 ? \"0.\" + new Array(-exponent).join(\"0\") + coefficient\n : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + \".\" + coefficient.slice(exponent + 1)\n : coefficient + new Array(exponent - coefficient.length + 2).join(\"0\");\n}\n","import formatPrefixAuto from \"./formatPrefixAuto.js\";\nimport formatRounded from \"./formatRounded.js\";\n\nexport default {\n \"%\": function(x, p) { return (x * 100).toFixed(p); },\n \"b\": function(x) { return Math.round(x).toString(2); },\n \"c\": function(x) { return x + \"\"; },\n \"d\": function(x) { return Math.round(x).toString(10); },\n \"e\": function(x, p) { return x.toExponential(p); },\n \"f\": function(x, p) { return x.toFixed(p); },\n \"g\": function(x, p) { return x.toPrecision(p); },\n \"o\": function(x) { return Math.round(x).toString(8); },\n \"p\": function(x, p) { return formatRounded(x * 100, p); },\n \"r\": formatRounded,\n \"s\": formatPrefixAuto,\n \"X\": function(x) { return Math.round(x).toString(16).toUpperCase(); },\n \"x\": function(x) { return Math.round(x).toString(16); }\n};\n","export default function(x) {\n return x;\n}\n","import exponent from \"./exponent.js\";\nimport formatGroup from \"./formatGroup.js\";\nimport formatNumerals from \"./formatNumerals.js\";\nimport formatSpecifier from \"./formatSpecifier.js\";\nimport formatTrim from \"./formatTrim.js\";\nimport formatTypes from \"./formatTypes.js\";\nimport {prefixExponent} from \"./formatPrefixAuto.js\";\nimport identity from \"./identity.js\";\n\nvar map = Array.prototype.map,\n prefixes = [\"y\",\"z\",\"a\",\"f\",\"p\",\"n\",\"µ\",\"m\",\"\",\"k\",\"M\",\"G\",\"T\",\"P\",\"E\",\"Z\",\"Y\"];\n\nexport default function(locale) {\n var group = locale.grouping === undefined || locale.thousands === undefined ? identity : formatGroup(map.call(locale.grouping, Number), locale.thousands + \"\"),\n currencyPrefix = locale.currency === undefined ? \"\" : locale.currency[0] + \"\",\n currencySuffix = locale.currency === undefined ? \"\" : locale.currency[1] + \"\",\n decimal = locale.decimal === undefined ? \".\" : locale.decimal + \"\",\n numerals = locale.numerals === undefined ? identity : formatNumerals(map.call(locale.numerals, String)),\n percent = locale.percent === undefined ? \"%\" : locale.percent + \"\",\n minus = locale.minus === undefined ? \"-\" : locale.minus + \"\",\n nan = locale.nan === undefined ? \"NaN\" : locale.nan + \"\";\n\n function newFormat(specifier) {\n specifier = formatSpecifier(specifier);\n\n var fill = specifier.fill,\n align = specifier.align,\n sign = specifier.sign,\n symbol = specifier.symbol,\n zero = specifier.zero,\n width = specifier.width,\n comma = specifier.comma,\n precision = specifier.precision,\n trim = specifier.trim,\n type = specifier.type;\n\n // The \"n\" type is an alias for \",g\".\n if (type === \"n\") comma = true, type = \"g\";\n\n // The \"\" type, and any invalid type, is an alias for \".12~g\".\n else if (!formatTypes[type]) precision === undefined && (precision = 12), trim = true, type = \"g\";\n\n // If zero fill is specified, padding goes after sign and before digits.\n if (zero || (fill === \"0\" && align === \"=\")) zero = true, fill = \"0\", align = \"=\";\n\n // Compute the prefix and suffix.\n // For SI-prefix, the suffix is lazily computed.\n var prefix = symbol === \"$\" ? currencyPrefix : symbol === \"#\" && /[boxX]/.test(type) ? \"0\" + type.toLowerCase() : \"\",\n suffix = symbol === \"$\" ? currencySuffix : /[%p]/.test(type) ? percent : \"\";\n\n // What format function should we use?\n // Is this an integer type?\n // Can this type generate exponential notation?\n var formatType = formatTypes[type],\n maybeSuffix = /[defgprs%]/.test(type);\n\n // Set the default precision if not specified,\n // or clamp the specified precision to the supported range.\n // For significant precision, it must be in [1, 21].\n // For fixed precision, it must be in [0, 20].\n precision = precision === undefined ? 6\n : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision))\n : Math.max(0, Math.min(20, precision));\n\n function format(value) {\n var valuePrefix = prefix,\n valueSuffix = suffix,\n i, n, c;\n\n if (type === \"c\") {\n valueSuffix = formatType(value) + valueSuffix;\n value = \"\";\n } else {\n value = +value;\n\n // Perform the initial formatting.\n var valueNegative = value < 0;\n value = isNaN(value) ? nan : formatType(Math.abs(value), precision);\n\n // Trim insignificant zeros.\n if (trim) value = formatTrim(value);\n\n // If a negative value rounds to zero during formatting, treat as positive.\n if (valueNegative && +value === 0) valueNegative = false;\n\n // Compute the prefix and suffix.\n valuePrefix = (valueNegative ? (sign === \"(\" ? sign : minus) : sign === \"-\" || sign === \"(\" ? \"\" : sign) + valuePrefix;\n\n valueSuffix = (type === \"s\" ? prefixes[8 + prefixExponent / 3] : \"\") + valueSuffix + (valueNegative && sign === \"(\" ? \")\" : \"\");\n\n // Break the formatted value into the integer “value” part that can be\n // grouped, and fractional or exponential “suffix” part that is not.\n if (maybeSuffix) {\n i = -1, n = value.length;\n while (++i < n) {\n if (c = value.charCodeAt(i), 48 > c || c > 57) {\n valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;\n value = value.slice(0, i);\n break;\n }\n }\n }\n }\n\n // If the fill character is not \"0\", grouping is applied before padding.\n if (comma && !zero) value = group(value, Infinity);\n\n // Compute the padding.\n var length = valuePrefix.length + value.length + valueSuffix.length,\n padding = length < width ? new Array(width - length + 1).join(fill) : \"\";\n\n // If the fill character is \"0\", grouping is applied after padding.\n if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = \"\";\n\n // Reconstruct the final output based on the desired alignment.\n switch (align) {\n case \"<\": value = valuePrefix + value + valueSuffix + padding; break;\n case \"=\": value = valuePrefix + padding + value + valueSuffix; break;\n case \"^\": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break;\n default: value = padding + valuePrefix + value + valueSuffix; break;\n }\n\n return numerals(value);\n }\n\n format.toString = function() {\n return specifier + \"\";\n };\n\n return format;\n }\n\n function formatPrefix(specifier, value) {\n var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = \"f\", specifier)),\n e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3,\n k = Math.pow(10, -e),\n prefix = prefixes[8 + e / 3];\n return function(value) {\n return f(k * value) + prefix;\n };\n }\n\n return {\n format: newFormat,\n formatPrefix: formatPrefix\n };\n}\n","import formatLocale from \"./locale.js\";\n\nvar locale;\nexport var format;\nexport var formatPrefix;\n\ndefaultLocale({\n decimal: \".\",\n thousands: \",\",\n grouping: [3],\n currency: [\"$\", \"\"],\n minus: \"-\"\n});\n\nexport default function defaultLocale(definition) {\n locale = formatLocale(definition);\n format = locale.format;\n formatPrefix = locale.formatPrefix;\n return locale;\n}\n","import exponent from \"./exponent.js\";\n\nexport default function(step) {\n return Math.max(0, -exponent(Math.abs(step)));\n}\n","import exponent from \"./exponent.js\";\n\nexport default function(step, value) {\n return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step)));\n}\n","import exponent from \"./exponent.js\";\n\nexport default function(step, max) {\n step = Math.abs(step), max = Math.abs(max) - step;\n return Math.max(0, exponent(max) - exponent(step)) + 1;\n}\n","var t0 = new Date,\n t1 = new Date;\n\nexport default function newInterval(floori, offseti, count, field) {\n\n function interval(date) {\n return floori(date = arguments.length === 0 ? new Date : new Date(+date)), date;\n }\n\n interval.floor = function(date) {\n return floori(date = new Date(+date)), date;\n };\n\n interval.ceil = function(date) {\n return floori(date = new Date(date - 1)), offseti(date, 1), floori(date), date;\n };\n\n interval.round = function(date) {\n var d0 = interval(date),\n d1 = interval.ceil(date);\n return date - d0 < d1 - date ? d0 : d1;\n };\n\n interval.offset = function(date, step) {\n return offseti(date = new Date(+date), step == null ? 1 : Math.floor(step)), date;\n };\n\n interval.range = function(start, stop, step) {\n var range = [], previous;\n start = interval.ceil(start);\n step = step == null ? 1 : Math.floor(step);\n if (!(start < stop) || !(step > 0)) return range; // also handles Invalid Date\n do range.push(previous = new Date(+start)), offseti(start, step), floori(start);\n while (previous < start && start < stop);\n return range;\n };\n\n interval.filter = function(test) {\n return newInterval(function(date) {\n if (date >= date) while (floori(date), !test(date)) date.setTime(date - 1);\n }, function(date, step) {\n if (date >= date) {\n if (step < 0) while (++step <= 0) {\n while (offseti(date, -1), !test(date)) {} // eslint-disable-line no-empty\n } else while (--step >= 0) {\n while (offseti(date, +1), !test(date)) {} // eslint-disable-line no-empty\n }\n }\n });\n };\n\n if (count) {\n interval.count = function(start, end) {\n t0.setTime(+start), t1.setTime(+end);\n floori(t0), floori(t1);\n return Math.floor(count(t0, t1));\n };\n\n interval.every = function(step) {\n step = Math.floor(step);\n return !isFinite(step) || !(step > 0) ? null\n : !(step > 1) ? interval\n : interval.filter(field\n ? function(d) { return field(d) % step === 0; }\n : function(d) { return interval.count(0, d) % step === 0; });\n };\n }\n\n return interval;\n}\n","import interval from \"./interval.js\";\n\nvar millisecond = interval(function() {\n // noop\n}, function(date, step) {\n date.setTime(+date + step);\n}, function(start, end) {\n return end - start;\n});\n\n// An optimized implementation for this simple case.\nmillisecond.every = function(k) {\n k = Math.floor(k);\n if (!isFinite(k) || !(k > 0)) return null;\n if (!(k > 1)) return millisecond;\n return interval(function(date) {\n date.setTime(Math.floor(date / k) * k);\n }, function(date, step) {\n date.setTime(+date + step * k);\n }, function(start, end) {\n return (end - start) / k;\n });\n};\n\nexport default millisecond;\nexport var milliseconds = millisecond.range;\n","export var durationSecond = 1e3;\nexport var durationMinute = 6e4;\nexport var durationHour = 36e5;\nexport var durationDay = 864e5;\nexport var durationWeek = 6048e5;\n","import interval from \"./interval.js\";\nimport {durationSecond} from \"./duration.js\";\n\nvar second = interval(function(date) {\n date.setTime(date - date.getMilliseconds());\n}, function(date, step) {\n date.setTime(+date + step * durationSecond);\n}, function(start, end) {\n return (end - start) / durationSecond;\n}, function(date) {\n return date.getUTCSeconds();\n});\n\nexport default second;\nexport var seconds = second.range;\n","import interval from \"./interval.js\";\nimport {durationDay, durationMinute} from \"./duration.js\";\n\nvar day = interval(function(date) {\n date.setHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setDate(date.getDate() + step);\n}, function(start, end) {\n return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationDay;\n}, function(date) {\n return date.getDate() - 1;\n});\n\nexport default day;\nexport var days = day.range;\n","import interval from \"./interval.js\";\nimport {durationMinute, durationWeek} from \"./duration.js\";\n\nfunction weekday(i) {\n return interval(function(date) {\n date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7);\n date.setHours(0, 0, 0, 0);\n }, function(date, step) {\n date.setDate(date.getDate() + step * 7);\n }, function(start, end) {\n return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationWeek;\n });\n}\n\nexport var sunday = weekday(0);\nexport var monday = weekday(1);\nexport var tuesday = weekday(2);\nexport var wednesday = weekday(3);\nexport var thursday = weekday(4);\nexport var friday = weekday(5);\nexport var saturday = weekday(6);\n\nexport var sundays = sunday.range;\nexport var mondays = monday.range;\nexport var tuesdays = tuesday.range;\nexport var wednesdays = wednesday.range;\nexport var thursdays = thursday.range;\nexport var fridays = friday.range;\nexport var saturdays = saturday.range;\n","import interval from \"./interval.js\";\n\nvar year = interval(function(date) {\n date.setMonth(0, 1);\n date.setHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setFullYear(date.getFullYear() + step);\n}, function(start, end) {\n return end.getFullYear() - start.getFullYear();\n}, function(date) {\n return date.getFullYear();\n});\n\n// An optimized implementation for this simple case.\nyear.every = function(k) {\n return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : interval(function(date) {\n date.setFullYear(Math.floor(date.getFullYear() / k) * k);\n date.setMonth(0, 1);\n date.setHours(0, 0, 0, 0);\n }, function(date, step) {\n date.setFullYear(date.getFullYear() + step * k);\n });\n};\n\nexport default year;\nexport var years = year.range;\n","import interval from \"./interval.js\";\nimport {durationMinute} from \"./duration.js\";\n\nvar utcMinute = interval(function(date) {\n date.setUTCSeconds(0, 0);\n}, function(date, step) {\n date.setTime(+date + step * durationMinute);\n}, function(start, end) {\n return (end - start) / durationMinute;\n}, function(date) {\n return date.getUTCMinutes();\n});\n\nexport default utcMinute;\nexport var utcMinutes = utcMinute.range;\n","import interval from \"./interval.js\";\nimport {durationHour} from \"./duration.js\";\n\nvar utcHour = interval(function(date) {\n date.setUTCMinutes(0, 0, 0);\n}, function(date, step) {\n date.setTime(+date + step * durationHour);\n}, function(start, end) {\n return (end - start) / durationHour;\n}, function(date) {\n return date.getUTCHours();\n});\n\nexport default utcHour;\nexport var utcHours = utcHour.range;\n","import interval from \"./interval.js\";\nimport {durationDay} from \"./duration.js\";\n\nvar utcDay = interval(function(date) {\n date.setUTCHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setUTCDate(date.getUTCDate() + step);\n}, function(start, end) {\n return (end - start) / durationDay;\n}, function(date) {\n return date.getUTCDate() - 1;\n});\n\nexport default utcDay;\nexport var utcDays = utcDay.range;\n","import interval from \"./interval.js\";\nimport {durationWeek} from \"./duration.js\";\n\nfunction utcWeekday(i) {\n return interval(function(date) {\n date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7);\n date.setUTCHours(0, 0, 0, 0);\n }, function(date, step) {\n date.setUTCDate(date.getUTCDate() + step * 7);\n }, function(start, end) {\n return (end - start) / durationWeek;\n });\n}\n\nexport var utcSunday = utcWeekday(0);\nexport var utcMonday = utcWeekday(1);\nexport var utcTuesday = utcWeekday(2);\nexport var utcWednesday = utcWeekday(3);\nexport var utcThursday = utcWeekday(4);\nexport var utcFriday = utcWeekday(5);\nexport var utcSaturday = utcWeekday(6);\n\nexport var utcSundays = utcSunday.range;\nexport var utcMondays = utcMonday.range;\nexport var utcTuesdays = utcTuesday.range;\nexport var utcWednesdays = utcWednesday.range;\nexport var utcThursdays = utcThursday.range;\nexport var utcFridays = utcFriday.range;\nexport var utcSaturdays = utcSaturday.range;\n","import interval from \"./interval.js\";\n\nvar utcMonth = interval(function(date) {\n date.setUTCDate(1);\n date.setUTCHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setUTCMonth(date.getUTCMonth() + step);\n}, function(start, end) {\n return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12;\n}, function(date) {\n return date.getUTCMonth();\n});\n\nexport default utcMonth;\nexport var utcMonths = utcMonth.range;\n","import interval from \"./interval.js\";\n\nvar utcYear = interval(function(date) {\n date.setUTCMonth(0, 1);\n date.setUTCHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setUTCFullYear(date.getUTCFullYear() + step);\n}, function(start, end) {\n return end.getUTCFullYear() - start.getUTCFullYear();\n}, function(date) {\n return date.getUTCFullYear();\n});\n\n// An optimized implementation for this simple case.\nutcYear.every = function(k) {\n return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : interval(function(date) {\n date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k);\n date.setUTCMonth(0, 1);\n date.setUTCHours(0, 0, 0, 0);\n }, function(date, step) {\n date.setUTCFullYear(date.getUTCFullYear() + step * k);\n });\n};\n\nexport default utcYear;\nexport var utcYears = utcYear.range;\n","import {\n timeDay,\n timeSunday,\n timeMonday,\n timeThursday,\n timeYear,\n utcDay,\n utcSunday,\n utcMonday,\n utcThursday,\n utcYear\n} from \"d3-time\";\n\nfunction localDate(d) {\n if (0 <= d.y && d.y < 100) {\n var date = new Date(-1, d.m, d.d, d.H, d.M, d.S, d.L);\n date.setFullYear(d.y);\n return date;\n }\n return new Date(d.y, d.m, d.d, d.H, d.M, d.S, d.L);\n}\n\nfunction utcDate(d) {\n if (0 <= d.y && d.y < 100) {\n var date = new Date(Date.UTC(-1, d.m, d.d, d.H, d.M, d.S, d.L));\n date.setUTCFullYear(d.y);\n return date;\n }\n return new Date(Date.UTC(d.y, d.m, d.d, d.H, d.M, d.S, d.L));\n}\n\nfunction newDate(y, m, d) {\n return {y: y, m: m, d: d, H: 0, M: 0, S: 0, L: 0};\n}\n\nexport default function formatLocale(locale) {\n var locale_dateTime = locale.dateTime,\n locale_date = locale.date,\n locale_time = locale.time,\n locale_periods = locale.periods,\n locale_weekdays = locale.days,\n locale_shortWeekdays = locale.shortDays,\n locale_months = locale.months,\n locale_shortMonths = locale.shortMonths;\n\n var periodRe = formatRe(locale_periods),\n periodLookup = formatLookup(locale_periods),\n weekdayRe = formatRe(locale_weekdays),\n weekdayLookup = formatLookup(locale_weekdays),\n shortWeekdayRe = formatRe(locale_shortWeekdays),\n shortWeekdayLookup = formatLookup(locale_shortWeekdays),\n monthRe = formatRe(locale_months),\n monthLookup = formatLookup(locale_months),\n shortMonthRe = formatRe(locale_shortMonths),\n shortMonthLookup = formatLookup(locale_shortMonths);\n\n var formats = {\n \"a\": formatShortWeekday,\n \"A\": formatWeekday,\n \"b\": formatShortMonth,\n \"B\": formatMonth,\n \"c\": null,\n \"d\": formatDayOfMonth,\n \"e\": formatDayOfMonth,\n \"f\": formatMicroseconds,\n \"H\": formatHour24,\n \"I\": formatHour12,\n \"j\": formatDayOfYear,\n \"L\": formatMilliseconds,\n \"m\": formatMonthNumber,\n \"M\": formatMinutes,\n \"p\": formatPeriod,\n \"q\": formatQuarter,\n \"Q\": formatUnixTimestamp,\n \"s\": formatUnixTimestampSeconds,\n \"S\": formatSeconds,\n \"u\": formatWeekdayNumberMonday,\n \"U\": formatWeekNumberSunday,\n \"V\": formatWeekNumberISO,\n \"w\": formatWeekdayNumberSunday,\n \"W\": formatWeekNumberMonday,\n \"x\": null,\n \"X\": null,\n \"y\": formatYear,\n \"Y\": formatFullYear,\n \"Z\": formatZone,\n \"%\": formatLiteralPercent\n };\n\n var utcFormats = {\n \"a\": formatUTCShortWeekday,\n \"A\": formatUTCWeekday,\n \"b\": formatUTCShortMonth,\n \"B\": formatUTCMonth,\n \"c\": null,\n \"d\": formatUTCDayOfMonth,\n \"e\": formatUTCDayOfMonth,\n \"f\": formatUTCMicroseconds,\n \"H\": formatUTCHour24,\n \"I\": formatUTCHour12,\n \"j\": formatUTCDayOfYear,\n \"L\": formatUTCMilliseconds,\n \"m\": formatUTCMonthNumber,\n \"M\": formatUTCMinutes,\n \"p\": formatUTCPeriod,\n \"q\": formatUTCQuarter,\n \"Q\": formatUnixTimestamp,\n \"s\": formatUnixTimestampSeconds,\n \"S\": formatUTCSeconds,\n \"u\": formatUTCWeekdayNumberMonday,\n \"U\": formatUTCWeekNumberSunday,\n \"V\": formatUTCWeekNumberISO,\n \"w\": formatUTCWeekdayNumberSunday,\n \"W\": formatUTCWeekNumberMonday,\n \"x\": null,\n \"X\": null,\n \"y\": formatUTCYear,\n \"Y\": formatUTCFullYear,\n \"Z\": formatUTCZone,\n \"%\": formatLiteralPercent\n };\n\n var parses = {\n \"a\": parseShortWeekday,\n \"A\": parseWeekday,\n \"b\": parseShortMonth,\n \"B\": parseMonth,\n \"c\": parseLocaleDateTime,\n \"d\": parseDayOfMonth,\n \"e\": parseDayOfMonth,\n \"f\": parseMicroseconds,\n \"H\": parseHour24,\n \"I\": parseHour24,\n \"j\": parseDayOfYear,\n \"L\": parseMilliseconds,\n \"m\": parseMonthNumber,\n \"M\": parseMinutes,\n \"p\": parsePeriod,\n \"q\": parseQuarter,\n \"Q\": parseUnixTimestamp,\n \"s\": parseUnixTimestampSeconds,\n \"S\": parseSeconds,\n \"u\": parseWeekdayNumberMonday,\n \"U\": parseWeekNumberSunday,\n \"V\": parseWeekNumberISO,\n \"w\": parseWeekdayNumberSunday,\n \"W\": parseWeekNumberMonday,\n \"x\": parseLocaleDate,\n \"X\": parseLocaleTime,\n \"y\": parseYear,\n \"Y\": parseFullYear,\n \"Z\": parseZone,\n \"%\": parseLiteralPercent\n };\n\n // These recursive directive definitions must be deferred.\n formats.x = newFormat(locale_date, formats);\n formats.X = newFormat(locale_time, formats);\n formats.c = newFormat(locale_dateTime, formats);\n utcFormats.x = newFormat(locale_date, utcFormats);\n utcFormats.X = newFormat(locale_time, utcFormats);\n utcFormats.c = newFormat(locale_dateTime, utcFormats);\n\n function newFormat(specifier, formats) {\n return function(date) {\n var string = [],\n i = -1,\n j = 0,\n n = specifier.length,\n c,\n pad,\n format;\n\n if (!(date instanceof Date)) date = new Date(+date);\n\n while (++i < n) {\n if (specifier.charCodeAt(i) === 37) {\n string.push(specifier.slice(j, i));\n if ((pad = pads[c = specifier.charAt(++i)]) != null) c = specifier.charAt(++i);\n else pad = c === \"e\" ? \" \" : \"0\";\n if (format = formats[c]) c = format(date, pad);\n string.push(c);\n j = i + 1;\n }\n }\n\n string.push(specifier.slice(j, i));\n return string.join(\"\");\n };\n }\n\n function newParse(specifier, Z) {\n return function(string) {\n var d = newDate(1900, undefined, 1),\n i = parseSpecifier(d, specifier, string += \"\", 0),\n week, day;\n if (i != string.length) return null;\n\n // If a UNIX timestamp is specified, return it.\n if (\"Q\" in d) return new Date(d.Q);\n if (\"s\" in d) return new Date(d.s * 1000 + (\"L\" in d ? d.L : 0));\n\n // If this is utcParse, never use the local timezone.\n if (Z && !(\"Z\" in d)) d.Z = 0;\n\n // The am-pm flag is 0 for AM, and 1 for PM.\n if (\"p\" in d) d.H = d.H % 12 + d.p * 12;\n\n // If the month was not specified, inherit from the quarter.\n if (d.m === undefined) d.m = \"q\" in d ? d.q : 0;\n\n // Convert day-of-week and week-of-year to day-of-year.\n if (\"V\" in d) {\n if (d.V < 1 || d.V > 53) return null;\n if (!(\"w\" in d)) d.w = 1;\n if (\"Z\" in d) {\n week = utcDate(newDate(d.y, 0, 1)), day = week.getUTCDay();\n week = day > 4 || day === 0 ? utcMonday.ceil(week) : utcMonday(week);\n week = utcDay.offset(week, (d.V - 1) * 7);\n d.y = week.getUTCFullYear();\n d.m = week.getUTCMonth();\n d.d = week.getUTCDate() + (d.w + 6) % 7;\n } else {\n week = localDate(newDate(d.y, 0, 1)), day = week.getDay();\n week = day > 4 || day === 0 ? timeMonday.ceil(week) : timeMonday(week);\n week = timeDay.offset(week, (d.V - 1) * 7);\n d.y = week.getFullYear();\n d.m = week.getMonth();\n d.d = week.getDate() + (d.w + 6) % 7;\n }\n } else if (\"W\" in d || \"U\" in d) {\n if (!(\"w\" in d)) d.w = \"u\" in d ? d.u % 7 : \"W\" in d ? 1 : 0;\n day = \"Z\" in d ? utcDate(newDate(d.y, 0, 1)).getUTCDay() : localDate(newDate(d.y, 0, 1)).getDay();\n d.m = 0;\n d.d = \"W\" in d ? (d.w + 6) % 7 + d.W * 7 - (day + 5) % 7 : d.w + d.U * 7 - (day + 6) % 7;\n }\n\n // If a time zone is specified, all fields are interpreted as UTC and then\n // offset according to the specified time zone.\n if (\"Z\" in d) {\n d.H += d.Z / 100 | 0;\n d.M += d.Z % 100;\n return utcDate(d);\n }\n\n // Otherwise, all fields are in local time.\n return localDate(d);\n };\n }\n\n function parseSpecifier(d, specifier, string, j) {\n var i = 0,\n n = specifier.length,\n m = string.length,\n c,\n parse;\n\n while (i < n) {\n if (j >= m) return -1;\n c = specifier.charCodeAt(i++);\n if (c === 37) {\n c = specifier.charAt(i++);\n parse = parses[c in pads ? specifier.charAt(i++) : c];\n if (!parse || ((j = parse(d, string, j)) < 0)) return -1;\n } else if (c != string.charCodeAt(j++)) {\n return -1;\n }\n }\n\n return j;\n }\n\n function parsePeriod(d, string, i) {\n var n = periodRe.exec(string.slice(i));\n return n ? (d.p = periodLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n }\n\n function parseShortWeekday(d, string, i) {\n var n = shortWeekdayRe.exec(string.slice(i));\n return n ? (d.w = shortWeekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n }\n\n function parseWeekday(d, string, i) {\n var n = weekdayRe.exec(string.slice(i));\n return n ? (d.w = weekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n }\n\n function parseShortMonth(d, string, i) {\n var n = shortMonthRe.exec(string.slice(i));\n return n ? (d.m = shortMonthLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n }\n\n function parseMonth(d, string, i) {\n var n = monthRe.exec(string.slice(i));\n return n ? (d.m = monthLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n }\n\n function parseLocaleDateTime(d, string, i) {\n return parseSpecifier(d, locale_dateTime, string, i);\n }\n\n function parseLocaleDate(d, string, i) {\n return parseSpecifier(d, locale_date, string, i);\n }\n\n function parseLocaleTime(d, string, i) {\n return parseSpecifier(d, locale_time, string, i);\n }\n\n function formatShortWeekday(d) {\n return locale_shortWeekdays[d.getDay()];\n }\n\n function formatWeekday(d) {\n return locale_weekdays[d.getDay()];\n }\n\n function formatShortMonth(d) {\n return locale_shortMonths[d.getMonth()];\n }\n\n function formatMonth(d) {\n return locale_months[d.getMonth()];\n }\n\n function formatPeriod(d) {\n return locale_periods[+(d.getHours() >= 12)];\n }\n\n function formatQuarter(d) {\n return 1 + ~~(d.getMonth() / 3);\n }\n\n function formatUTCShortWeekday(d) {\n return locale_shortWeekdays[d.getUTCDay()];\n }\n\n function formatUTCWeekday(d) {\n return locale_weekdays[d.getUTCDay()];\n }\n\n function formatUTCShortMonth(d) {\n return locale_shortMonths[d.getUTCMonth()];\n }\n\n function formatUTCMonth(d) {\n return locale_months[d.getUTCMonth()];\n }\n\n function formatUTCPeriod(d) {\n return locale_periods[+(d.getUTCHours() >= 12)];\n }\n\n function formatUTCQuarter(d) {\n return 1 + ~~(d.getUTCMonth() / 3);\n }\n\n return {\n format: function(specifier) {\n var f = newFormat(specifier += \"\", formats);\n f.toString = function() { return specifier; };\n return f;\n },\n parse: function(specifier) {\n var p = newParse(specifier += \"\", false);\n p.toString = function() { return specifier; };\n return p;\n },\n utcFormat: function(specifier) {\n var f = newFormat(specifier += \"\", utcFormats);\n f.toString = function() { return specifier; };\n return f;\n },\n utcParse: function(specifier) {\n var p = newParse(specifier += \"\", true);\n p.toString = function() { return specifier; };\n return p;\n }\n };\n}\n\nvar pads = {\"-\": \"\", \"_\": \" \", \"0\": \"0\"},\n numberRe = /^\\s*\\d+/, // note: ignores next directive\n percentRe = /^%/,\n requoteRe = /[\\\\^$*+?|[\\]().{}]/g;\n\nfunction pad(value, fill, width) {\n var sign = value < 0 ? \"-\" : \"\",\n string = (sign ? -value : value) + \"\",\n length = string.length;\n return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);\n}\n\nfunction requote(s) {\n return s.replace(requoteRe, \"\\\\$&\");\n}\n\nfunction formatRe(names) {\n return new RegExp(\"^(?:\" + names.map(requote).join(\"|\") + \")\", \"i\");\n}\n\nfunction formatLookup(names) {\n var map = {}, i = -1, n = names.length;\n while (++i < n) map[names[i].toLowerCase()] = i;\n return map;\n}\n\nfunction parseWeekdayNumberSunday(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 1));\n return n ? (d.w = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseWeekdayNumberMonday(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 1));\n return n ? (d.u = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseWeekNumberSunday(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.U = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseWeekNumberISO(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.V = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseWeekNumberMonday(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.W = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseFullYear(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 4));\n return n ? (d.y = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseYear(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.y = +n[0] + (+n[0] > 68 ? 1900 : 2000), i + n[0].length) : -1;\n}\n\nfunction parseZone(d, string, i) {\n var n = /^(Z)|([+-]\\d\\d)(?::?(\\d\\d))?/.exec(string.slice(i, i + 6));\n return n ? (d.Z = n[1] ? 0 : -(n[2] + (n[3] || \"00\")), i + n[0].length) : -1;\n}\n\nfunction parseQuarter(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 1));\n return n ? (d.q = n[0] * 3 - 3, i + n[0].length) : -1;\n}\n\nfunction parseMonthNumber(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.m = n[0] - 1, i + n[0].length) : -1;\n}\n\nfunction parseDayOfMonth(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.d = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseDayOfYear(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 3));\n return n ? (d.m = 0, d.d = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseHour24(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.H = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseMinutes(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.M = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseSeconds(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.S = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseMilliseconds(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 3));\n return n ? (d.L = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseMicroseconds(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 6));\n return n ? (d.L = Math.floor(n[0] / 1000), i + n[0].length) : -1;\n}\n\nfunction parseLiteralPercent(d, string, i) {\n var n = percentRe.exec(string.slice(i, i + 1));\n return n ? i + n[0].length : -1;\n}\n\nfunction parseUnixTimestamp(d, string, i) {\n var n = numberRe.exec(string.slice(i));\n return n ? (d.Q = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseUnixTimestampSeconds(d, string, i) {\n var n = numberRe.exec(string.slice(i));\n return n ? (d.s = +n[0], i + n[0].length) : -1;\n}\n\nfunction formatDayOfMonth(d, p) {\n return pad(d.getDate(), p, 2);\n}\n\nfunction formatHour24(d, p) {\n return pad(d.getHours(), p, 2);\n}\n\nfunction formatHour12(d, p) {\n return pad(d.getHours() % 12 || 12, p, 2);\n}\n\nfunction formatDayOfYear(d, p) {\n return pad(1 + timeDay.count(timeYear(d), d), p, 3);\n}\n\nfunction formatMilliseconds(d, p) {\n return pad(d.getMilliseconds(), p, 3);\n}\n\nfunction formatMicroseconds(d, p) {\n return formatMilliseconds(d, p) + \"000\";\n}\n\nfunction formatMonthNumber(d, p) {\n return pad(d.getMonth() + 1, p, 2);\n}\n\nfunction formatMinutes(d, p) {\n return pad(d.getMinutes(), p, 2);\n}\n\nfunction formatSeconds(d, p) {\n return pad(d.getSeconds(), p, 2);\n}\n\nfunction formatWeekdayNumberMonday(d) {\n var day = d.getDay();\n return day === 0 ? 7 : day;\n}\n\nfunction formatWeekNumberSunday(d, p) {\n return pad(timeSunday.count(timeYear(d) - 1, d), p, 2);\n}\n\nfunction formatWeekNumberISO(d, p) {\n var day = d.getDay();\n d = (day >= 4 || day === 0) ? timeThursday(d) : timeThursday.ceil(d);\n return pad(timeThursday.count(timeYear(d), d) + (timeYear(d).getDay() === 4), p, 2);\n}\n\nfunction formatWeekdayNumberSunday(d) {\n return d.getDay();\n}\n\nfunction formatWeekNumberMonday(d, p) {\n return pad(timeMonday.count(timeYear(d) - 1, d), p, 2);\n}\n\nfunction formatYear(d, p) {\n return pad(d.getFullYear() % 100, p, 2);\n}\n\nfunction formatFullYear(d, p) {\n return pad(d.getFullYear() % 10000, p, 4);\n}\n\nfunction formatZone(d) {\n var z = d.getTimezoneOffset();\n return (z > 0 ? \"-\" : (z *= -1, \"+\"))\n + pad(z / 60 | 0, \"0\", 2)\n + pad(z % 60, \"0\", 2);\n}\n\nfunction formatUTCDayOfMonth(d, p) {\n return pad(d.getUTCDate(), p, 2);\n}\n\nfunction formatUTCHour24(d, p) {\n return pad(d.getUTCHours(), p, 2);\n}\n\nfunction formatUTCHour12(d, p) {\n return pad(d.getUTCHours() % 12 || 12, p, 2);\n}\n\nfunction formatUTCDayOfYear(d, p) {\n return pad(1 + utcDay.count(utcYear(d), d), p, 3);\n}\n\nfunction formatUTCMilliseconds(d, p) {\n return pad(d.getUTCMilliseconds(), p, 3);\n}\n\nfunction formatUTCMicroseconds(d, p) {\n return formatUTCMilliseconds(d, p) + \"000\";\n}\n\nfunction formatUTCMonthNumber(d, p) {\n return pad(d.getUTCMonth() + 1, p, 2);\n}\n\nfunction formatUTCMinutes(d, p) {\n return pad(d.getUTCMinutes(), p, 2);\n}\n\nfunction formatUTCSeconds(d, p) {\n return pad(d.getUTCSeconds(), p, 2);\n}\n\nfunction formatUTCWeekdayNumberMonday(d) {\n var dow = d.getUTCDay();\n return dow === 0 ? 7 : dow;\n}\n\nfunction formatUTCWeekNumberSunday(d, p) {\n return pad(utcSunday.count(utcYear(d) - 1, d), p, 2);\n}\n\nfunction formatUTCWeekNumberISO(d, p) {\n var day = d.getUTCDay();\n d = (day >= 4 || day === 0) ? utcThursday(d) : utcThursday.ceil(d);\n return pad(utcThursday.count(utcYear(d), d) + (utcYear(d).getUTCDay() === 4), p, 2);\n}\n\nfunction formatUTCWeekdayNumberSunday(d) {\n return d.getUTCDay();\n}\n\nfunction formatUTCWeekNumberMonday(d, p) {\n return pad(utcMonday.count(utcYear(d) - 1, d), p, 2);\n}\n\nfunction formatUTCYear(d, p) {\n return pad(d.getUTCFullYear() % 100, p, 2);\n}\n\nfunction formatUTCFullYear(d, p) {\n return pad(d.getUTCFullYear() % 10000, p, 4);\n}\n\nfunction formatUTCZone() {\n return \"+0000\";\n}\n\nfunction formatLiteralPercent() {\n return \"%\";\n}\n\nfunction formatUnixTimestamp(d) {\n return +d;\n}\n\nfunction formatUnixTimestampSeconds(d) {\n return Math.floor(+d / 1000);\n}\n","import formatLocale from \"./locale.js\";\n\nvar locale;\nexport var timeFormat;\nexport var timeParse;\nexport var utcFormat;\nexport var utcParse;\n\ndefaultLocale({\n dateTime: \"%x, %X\",\n date: \"%-m/%-d/%Y\",\n time: \"%-I:%M:%S %p\",\n periods: [\"AM\", \"PM\"],\n days: [\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"],\n shortDays: [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"],\n months: [\"January\", \"February\", \"March\", \"April\", \"May\", \"June\", \"July\", \"August\", \"September\", \"October\", \"November\", \"December\"],\n shortMonths: [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\n});\n\nexport default function defaultLocale(definition) {\n locale = formatLocale(definition);\n timeFormat = locale.format;\n timeParse = locale.parse;\n utcFormat = locale.utcFormat;\n utcParse = locale.utcParse;\n return locale;\n}\n","/**\n * Helper functions to format numbers and dates.\n */\n\nimport { format } from \"d3-format\";\nimport { utcFormat, timeFormat } from \"d3-time-format\";\nimport { derived } from \"svelte/store\";\n\nimport { favaAPIStore, interval } from \"./stores\";\n\nlet formatter: (num: number) => string;\nlet incognito: (num: string) => string;\n\nfavaAPIStore.subscribe(favaAPI => {\n const { locale } = favaAPI.favaOptions;\n formatter = locale\n ? new Intl.NumberFormat(locale.replace(\"_\", \"-\")).format\n : (formatter = format(\".2f\"));\n incognito = favaAPI.incognito\n ? num => num.replace(/[0-9]/g, \"X\")\n : num => num;\n});\n\nexport function formatCurrency(number: number): string {\n return incognito(formatter(number));\n}\n\nconst formatterPer = format(\".2f\");\nexport function formatPercentage(number: number) {\n return `${formatterPer(Math.abs(number) * 100)}%`;\n}\n\nconst formatterShort = format(\".2s\");\nexport function formatCurrencyShort(number: number) {\n return incognito(formatterShort(number));\n}\n\n/** Date formatters for human consumption. */\nexport const dateFormat = {\n year: utcFormat(\"%Y\"),\n quarter(date: Date) {\n return `${date.getUTCFullYear()}Q${Math.floor(date.getUTCMonth() / 3) + 1}`;\n },\n month: utcFormat(\"%b %Y\"),\n week: utcFormat(\"%YW%W\"),\n day: utcFormat(\"%Y-%m-%d\"),\n};\n\n/** Date formatters for the entry filter form. */\nexport const timeFilterDateFormat = {\n year: utcFormat(\"%Y\"),\n quarter(date: Date) {\n return `${date.getUTCFullYear()}-Q${Math.floor(date.getUTCMonth() / 3) +\n 1}`;\n },\n month: utcFormat(\"%Y-%m\"),\n week: utcFormat(\"%Y-W%W\"),\n day: utcFormat(\"%Y-%m-%d\"),\n};\n\n/** Today as a ISO-8601 date string. */\nexport function todayAsString(): string {\n return timeFormat(\"%Y-%m-%d\")(new Date());\n}\nexport const currentDateFormat = derived(interval, val => dateFormat[val]);\nexport const currentTimeFilterDateFormat = derived(\n interval,\n val => timeFilterDateFormat[val]\n);\n","export var slice = Array.prototype.slice;\n","export default function(x) {\n return x;\n}\n","import {slice} from \"./array\";\nimport identity from \"./identity\";\n\nvar top = 1,\n right = 2,\n bottom = 3,\n left = 4,\n epsilon = 1e-6;\n\nfunction translateX(x) {\n return \"translate(\" + (x + 0.5) + \",0)\";\n}\n\nfunction translateY(y) {\n return \"translate(0,\" + (y + 0.5) + \")\";\n}\n\nfunction number(scale) {\n return function(d) {\n return +scale(d);\n };\n}\n\nfunction center(scale) {\n var offset = Math.max(0, scale.bandwidth() - 1) / 2; // Adjust for 0.5px offset.\n if (scale.round()) offset = Math.round(offset);\n return function(d) {\n return +scale(d) + offset;\n };\n}\n\nfunction entering() {\n return !this.__axis;\n}\n\nfunction axis(orient, scale) {\n var tickArguments = [],\n tickValues = null,\n tickFormat = null,\n tickSizeInner = 6,\n tickSizeOuter = 6,\n tickPadding = 3,\n k = orient === top || orient === left ? -1 : 1,\n x = orient === left || orient === right ? \"x\" : \"y\",\n transform = orient === top || orient === bottom ? translateX : translateY;\n\n function axis(context) {\n var values = tickValues == null ? (scale.ticks ? scale.ticks.apply(scale, tickArguments) : scale.domain()) : tickValues,\n format = tickFormat == null ? (scale.tickFormat ? scale.tickFormat.apply(scale, tickArguments) : identity) : tickFormat,\n spacing = Math.max(tickSizeInner, 0) + tickPadding,\n range = scale.range(),\n range0 = +range[0] + 0.5,\n range1 = +range[range.length - 1] + 0.5,\n position = (scale.bandwidth ? center : number)(scale.copy()),\n selection = context.selection ? context.selection() : context,\n path = selection.selectAll(\".domain\").data([null]),\n tick = selection.selectAll(\".tick\").data(values, scale).order(),\n tickExit = tick.exit(),\n tickEnter = tick.enter().append(\"g\").attr(\"class\", \"tick\"),\n line = tick.select(\"line\"),\n text = tick.select(\"text\");\n\n path = path.merge(path.enter().insert(\"path\", \".tick\")\n .attr(\"class\", \"domain\")\n .attr(\"stroke\", \"currentColor\"));\n\n tick = tick.merge(tickEnter);\n\n line = line.merge(tickEnter.append(\"line\")\n .attr(\"stroke\", \"currentColor\")\n .attr(x + \"2\", k * tickSizeInner));\n\n text = text.merge(tickEnter.append(\"text\")\n .attr(\"fill\", \"currentColor\")\n .attr(x, k * spacing)\n .attr(\"dy\", orient === top ? \"0em\" : orient === bottom ? \"0.71em\" : \"0.32em\"));\n\n if (context !== selection) {\n path = path.transition(context);\n tick = tick.transition(context);\n line = line.transition(context);\n text = text.transition(context);\n\n tickExit = tickExit.transition(context)\n .attr(\"opacity\", epsilon)\n .attr(\"transform\", function(d) { return isFinite(d = position(d)) ? transform(d) : this.getAttribute(\"transform\"); });\n\n tickEnter\n .attr(\"opacity\", epsilon)\n .attr(\"transform\", function(d) { var p = this.parentNode.__axis; return transform(p && isFinite(p = p(d)) ? p : position(d)); });\n }\n\n tickExit.remove();\n\n path\n .attr(\"d\", orient === left || orient == right\n ? (tickSizeOuter ? \"M\" + k * tickSizeOuter + \",\" + range0 + \"H0.5V\" + range1 + \"H\" + k * tickSizeOuter : \"M0.5,\" + range0 + \"V\" + range1)\n : (tickSizeOuter ? \"M\" + range0 + \",\" + k * tickSizeOuter + \"V0.5H\" + range1 + \"V\" + k * tickSizeOuter : \"M\" + range0 + \",0.5H\" + range1));\n\n tick\n .attr(\"opacity\", 1)\n .attr(\"transform\", function(d) { return transform(position(d)); });\n\n line\n .attr(x + \"2\", k * tickSizeInner);\n\n text\n .attr(x, k * spacing)\n .text(format);\n\n selection.filter(entering)\n .attr(\"fill\", \"none\")\n .attr(\"font-size\", 10)\n .attr(\"font-family\", \"sans-serif\")\n .attr(\"text-anchor\", orient === right ? \"start\" : orient === left ? \"end\" : \"middle\");\n\n selection\n .each(function() { this.__axis = position; });\n }\n\n axis.scale = function(_) {\n return arguments.length ? (scale = _, axis) : scale;\n };\n\n axis.ticks = function() {\n return tickArguments = slice.call(arguments), axis;\n };\n\n axis.tickArguments = function(_) {\n return arguments.length ? (tickArguments = _ == null ? [] : slice.call(_), axis) : tickArguments.slice();\n };\n\n axis.tickValues = function(_) {\n return arguments.length ? (tickValues = _ == null ? null : slice.call(_), axis) : tickValues && tickValues.slice();\n };\n\n axis.tickFormat = function(_) {\n return arguments.length ? (tickFormat = _, axis) : tickFormat;\n };\n\n axis.tickSize = function(_) {\n return arguments.length ? (tickSizeInner = tickSizeOuter = +_, axis) : tickSizeInner;\n };\n\n axis.tickSizeInner = function(_) {\n return arguments.length ? (tickSizeInner = +_, axis) : tickSizeInner;\n };\n\n axis.tickSizeOuter = function(_) {\n return arguments.length ? (tickSizeOuter = +_, axis) : tickSizeOuter;\n };\n\n axis.tickPadding = function(_) {\n return arguments.length ? (tickPadding = +_, axis) : tickPadding;\n };\n\n return axis;\n}\n\nexport function axisTop(scale) {\n return axis(top, scale);\n}\n\nexport function axisRight(scale) {\n return axis(right, scale);\n}\n\nexport function axisBottom(scale) {\n return axis(bottom, scale);\n}\n\nexport function axisLeft(scale) {\n return axis(left, scale);\n}\n","export function initRange(domain, range) {\n switch (arguments.length) {\n case 0: break;\n case 1: this.range(domain); break;\n default: this.range(range).domain(domain); break;\n }\n return this;\n}\n\nexport function initInterpolator(domain, interpolator) {\n switch (arguments.length) {\n case 0: break;\n case 1: {\n if (typeof domain === \"function\") this.interpolator(domain);\n else this.range(domain);\n break;\n }\n default: {\n this.domain(domain);\n if (typeof interpolator === \"function\") this.interpolator(interpolator);\n else this.range(interpolator);\n break;\n }\n }\n return this;\n}\n","import {initRange} from \"./init.js\";\n\nexport const implicit = Symbol(\"implicit\");\n\nexport default function ordinal() {\n var index = new Map(),\n domain = [],\n range = [],\n unknown = implicit;\n\n function scale(d) {\n var key = d + \"\", i = index.get(key);\n if (!i) {\n if (unknown !== implicit) return unknown;\n index.set(key, i = domain.push(d));\n }\n return range[(i - 1) % range.length];\n }\n\n scale.domain = function(_) {\n if (!arguments.length) return domain.slice();\n domain = [], index = new Map();\n for (const value of _) {\n const key = value + \"\";\n if (index.has(key)) continue;\n index.set(key, domain.push(value));\n }\n return scale;\n };\n\n scale.range = function(_) {\n return arguments.length ? (range = Array.from(_), scale) : range.slice();\n };\n\n scale.unknown = function(_) {\n return arguments.length ? (unknown = _, scale) : unknown;\n };\n\n scale.copy = function() {\n return ordinal(domain, range).unknown(unknown);\n };\n\n initRange.apply(scale, arguments);\n\n return scale;\n}\n","import {range as sequence} from \"d3-array\";\nimport {initRange} from \"./init.js\";\nimport ordinal from \"./ordinal.js\";\n\nexport default function band() {\n var scale = ordinal().unknown(undefined),\n domain = scale.domain,\n ordinalRange = scale.range,\n r0 = 0,\n r1 = 1,\n step,\n bandwidth,\n round = false,\n paddingInner = 0,\n paddingOuter = 0,\n align = 0.5;\n\n delete scale.unknown;\n\n function rescale() {\n var n = domain().length,\n reverse = r1 < r0,\n start = reverse ? r1 : r0,\n stop = reverse ? r0 : r1;\n step = (stop - start) / Math.max(1, n - paddingInner + paddingOuter * 2);\n if (round) step = Math.floor(step);\n start += (stop - start - step * (n - paddingInner)) * align;\n bandwidth = step * (1 - paddingInner);\n if (round) start = Math.round(start), bandwidth = Math.round(bandwidth);\n var values = sequence(n).map(function(i) { return start + step * i; });\n return ordinalRange(reverse ? values.reverse() : values);\n }\n\n scale.domain = function(_) {\n return arguments.length ? (domain(_), rescale()) : domain();\n };\n\n scale.range = function(_) {\n return arguments.length ? ([r0, r1] = _, r0 = +r0, r1 = +r1, rescale()) : [r0, r1];\n };\n\n scale.rangeRound = function(_) {\n return [r0, r1] = _, r0 = +r0, r1 = +r1, round = true, rescale();\n };\n\n scale.bandwidth = function() {\n return bandwidth;\n };\n\n scale.step = function() {\n return step;\n };\n\n scale.round = function(_) {\n return arguments.length ? (round = !!_, rescale()) : round;\n };\n\n scale.padding = function(_) {\n return arguments.length ? (paddingInner = Math.min(1, paddingOuter = +_), rescale()) : paddingInner;\n };\n\n scale.paddingInner = function(_) {\n return arguments.length ? (paddingInner = Math.min(1, _), rescale()) : paddingInner;\n };\n\n scale.paddingOuter = function(_) {\n return arguments.length ? (paddingOuter = +_, rescale()) : paddingOuter;\n };\n\n scale.align = function(_) {\n return arguments.length ? (align = Math.max(0, Math.min(1, _)), rescale()) : align;\n };\n\n scale.copy = function() {\n return band(domain(), [r0, r1])\n .round(round)\n .paddingInner(paddingInner)\n .paddingOuter(paddingOuter)\n .align(align);\n };\n\n return initRange.apply(rescale(), arguments);\n}\n\nfunction pointish(scale) {\n var copy = scale.copy;\n\n scale.padding = scale.paddingOuter;\n delete scale.paddingInner;\n delete scale.paddingOuter;\n\n scale.copy = function() {\n return pointish(copy());\n };\n\n return scale;\n}\n\nexport function point() {\n return pointish(band.apply(null, arguments).paddingInner(1));\n}\n","export default function(x) {\n return function() {\n return x;\n };\n}\n","export default function(x) {\n return +x;\n}\n","import {bisect} from \"d3-array\";\nimport {interpolate as interpolateValue, interpolateNumber, interpolateRound} from \"d3-interpolate\";\nimport constant from \"./constant.js\";\nimport number from \"./number.js\";\n\nvar unit = [0, 1];\n\nexport function identity(x) {\n return x;\n}\n\nfunction normalize(a, b) {\n return (b -= (a = +a))\n ? function(x) { return (x - a) / b; }\n : constant(isNaN(b) ? NaN : 0.5);\n}\n\nfunction clamper(a, b) {\n var t;\n if (a > b) t = a, a = b, b = t;\n return function(x) { return Math.max(a, Math.min(b, x)); };\n}\n\n// normalize(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].\n// interpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding range value x in [a,b].\nfunction bimap(domain, range, interpolate) {\n var d0 = domain[0], d1 = domain[1], r0 = range[0], r1 = range[1];\n if (d1 < d0) d0 = normalize(d1, d0), r0 = interpolate(r1, r0);\n else d0 = normalize(d0, d1), r0 = interpolate(r0, r1);\n return function(x) { return r0(d0(x)); };\n}\n\nfunction polymap(domain, range, interpolate) {\n var j = Math.min(domain.length, range.length) - 1,\n d = new Array(j),\n r = new Array(j),\n i = -1;\n\n // Reverse descending domains.\n if (domain[j] < domain[0]) {\n domain = domain.slice().reverse();\n range = range.slice().reverse();\n }\n\n while (++i < j) {\n d[i] = normalize(domain[i], domain[i + 1]);\n r[i] = interpolate(range[i], range[i + 1]);\n }\n\n return function(x) {\n var i = bisect(domain, x, 1, j) - 1;\n return r[i](d[i](x));\n };\n}\n\nexport function copy(source, target) {\n return target\n .domain(source.domain())\n .range(source.range())\n .interpolate(source.interpolate())\n .clamp(source.clamp())\n .unknown(source.unknown());\n}\n\nexport function transformer() {\n var domain = unit,\n range = unit,\n interpolate = interpolateValue,\n transform,\n untransform,\n unknown,\n clamp = identity,\n piecewise,\n output,\n input;\n\n function rescale() {\n var n = Math.min(domain.length, range.length);\n if (clamp !== identity) clamp = clamper(domain[0], domain[n - 1]);\n piecewise = n > 2 ? polymap : bimap;\n output = input = null;\n return scale;\n }\n\n function scale(x) {\n return isNaN(x = +x) ? unknown : (output || (output = piecewise(domain.map(transform), range, interpolate)))(transform(clamp(x)));\n }\n\n scale.invert = function(y) {\n return clamp(untransform((input || (input = piecewise(range, domain.map(transform), interpolateNumber)))(y)));\n };\n\n scale.domain = function(_) {\n return arguments.length ? (domain = Array.from(_, number), rescale()) : domain.slice();\n };\n\n scale.range = function(_) {\n return arguments.length ? (range = Array.from(_), rescale()) : range.slice();\n };\n\n scale.rangeRound = function(_) {\n return range = Array.from(_), interpolate = interpolateRound, rescale();\n };\n\n scale.clamp = function(_) {\n return arguments.length ? (clamp = _ ? true : identity, rescale()) : clamp !== identity;\n };\n\n scale.interpolate = function(_) {\n return arguments.length ? (interpolate = _, rescale()) : interpolate;\n };\n\n scale.unknown = function(_) {\n return arguments.length ? (unknown = _, scale) : unknown;\n };\n\n return function(t, u) {\n transform = t, untransform = u;\n return rescale();\n };\n}\n\nexport default function continuous() {\n return transformer()(identity, identity);\n}\n","import {tickStep} from \"d3-array\";\nimport {format, formatPrefix, formatSpecifier, precisionFixed, precisionPrefix, precisionRound} from \"d3-format\";\n\nexport default function(start, stop, count, specifier) {\n var step = tickStep(start, stop, count),\n precision;\n specifier = formatSpecifier(specifier == null ? \",f\" : specifier);\n switch (specifier.type) {\n case \"s\": {\n var value = Math.max(Math.abs(start), Math.abs(stop));\n if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision;\n return formatPrefix(specifier, value);\n }\n case \"\":\n case \"e\":\n case \"g\":\n case \"p\":\n case \"r\": {\n if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === \"e\");\n break;\n }\n case \"f\":\n case \"%\": {\n if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === \"%\") * 2;\n break;\n }\n }\n return format(specifier);\n}\n","import {ticks, tickIncrement} from \"d3-array\";\nimport continuous, {copy} from \"./continuous.js\";\nimport {initRange} from \"./init.js\";\nimport tickFormat from \"./tickFormat.js\";\n\nexport function linearish(scale) {\n var domain = scale.domain;\n\n scale.ticks = function(count) {\n var d = domain();\n return ticks(d[0], d[d.length - 1], count == null ? 10 : count);\n };\n\n scale.tickFormat = function(count, specifier) {\n var d = domain();\n return tickFormat(d[0], d[d.length - 1], count == null ? 10 : count, specifier);\n };\n\n scale.nice = function(count) {\n if (count == null) count = 10;\n\n var d = domain(),\n i0 = 0,\n i1 = d.length - 1,\n start = d[i0],\n stop = d[i1],\n step;\n\n if (stop < start) {\n step = start, start = stop, stop = step;\n step = i0, i0 = i1, i1 = step;\n }\n\n step = tickIncrement(start, stop, count);\n\n if (step > 0) {\n start = Math.floor(start / step) * step;\n stop = Math.ceil(stop / step) * step;\n step = tickIncrement(start, stop, count);\n } else if (step < 0) {\n start = Math.ceil(start * step) / step;\n stop = Math.floor(stop * step) / step;\n step = tickIncrement(start, stop, count);\n }\n\n if (step > 0) {\n d[i0] = Math.floor(start / step) * step;\n d[i1] = Math.ceil(stop / step) * step;\n domain(d);\n } else if (step < 0) {\n d[i0] = Math.ceil(start * step) / step;\n d[i1] = Math.floor(stop * step) / step;\n domain(d);\n }\n\n return scale;\n };\n\n return scale;\n}\n\nexport default function linear() {\n var scale = continuous();\n\n scale.copy = function() {\n return copy(scale, linear());\n };\n\n initRange.apply(scale, arguments);\n\n return linearish(scale);\n}\n","export default function(domain, interval) {\n domain = domain.slice();\n\n var i0 = 0,\n i1 = domain.length - 1,\n x0 = domain[i0],\n x1 = domain[i1],\n t;\n\n if (x1 < x0) {\n t = i0, i0 = i1, i1 = t;\n t = x0, x0 = x1, x1 = t;\n }\n\n domain[i0] = interval.floor(x0);\n domain[i1] = interval.ceil(x1);\n return domain;\n}\n","import {linearish} from \"./linear.js\";\nimport {copy, identity, transformer} from \"./continuous.js\";\nimport {initRange} from \"./init.js\";\n\nfunction transformPow(exponent) {\n return function(x) {\n return x < 0 ? -Math.pow(-x, exponent) : Math.pow(x, exponent);\n };\n}\n\nfunction transformSqrt(x) {\n return x < 0 ? -Math.sqrt(-x) : Math.sqrt(x);\n}\n\nfunction transformSquare(x) {\n return x < 0 ? -x * x : x * x;\n}\n\nexport function powish(transform) {\n var scale = transform(identity, identity),\n exponent = 1;\n\n function rescale() {\n return exponent === 1 ? transform(identity, identity)\n : exponent === 0.5 ? transform(transformSqrt, transformSquare)\n : transform(transformPow(exponent), transformPow(1 / exponent));\n }\n\n scale.exponent = function(_) {\n return arguments.length ? (exponent = +_, rescale()) : exponent;\n };\n\n return linearish(scale);\n}\n\nexport default function pow() {\n var scale = powish(transformer());\n\n scale.copy = function() {\n return copy(scale, pow()).exponent(scale.exponent());\n };\n\n initRange.apply(scale, arguments);\n\n return scale;\n}\n\nexport function sqrt() {\n return pow.apply(null, arguments).exponent(0.5);\n}\n","import {bisector, tickStep} from \"d3-array\";\nimport {timeYear, timeMonth, timeWeek, timeDay, timeHour, timeMinute, timeSecond, timeMillisecond} from \"d3-time\";\nimport {timeFormat} from \"d3-time-format\";\nimport continuous, {copy} from \"./continuous.js\";\nimport {initRange} from \"./init.js\";\nimport nice from \"./nice.js\";\n\nvar durationSecond = 1000,\n durationMinute = durationSecond * 60,\n durationHour = durationMinute * 60,\n durationDay = durationHour * 24,\n durationWeek = durationDay * 7,\n durationMonth = durationDay * 30,\n durationYear = durationDay * 365;\n\nfunction date(t) {\n return new Date(t);\n}\n\nfunction number(t) {\n return t instanceof Date ? +t : +new Date(+t);\n}\n\nexport function calendar(year, month, week, day, hour, minute, second, millisecond, format) {\n var scale = continuous(),\n invert = scale.invert,\n domain = scale.domain;\n\n var formatMillisecond = format(\".%L\"),\n formatSecond = format(\":%S\"),\n formatMinute = format(\"%I:%M\"),\n formatHour = format(\"%I %p\"),\n formatDay = format(\"%a %d\"),\n formatWeek = format(\"%b %d\"),\n formatMonth = format(\"%B\"),\n formatYear = format(\"%Y\");\n\n var tickIntervals = [\n [second, 1, durationSecond],\n [second, 5, 5 * durationSecond],\n [second, 15, 15 * durationSecond],\n [second, 30, 30 * durationSecond],\n [minute, 1, durationMinute],\n [minute, 5, 5 * durationMinute],\n [minute, 15, 15 * durationMinute],\n [minute, 30, 30 * durationMinute],\n [ hour, 1, durationHour ],\n [ hour, 3, 3 * durationHour ],\n [ hour, 6, 6 * durationHour ],\n [ hour, 12, 12 * durationHour ],\n [ day, 1, durationDay ],\n [ day, 2, 2 * durationDay ],\n [ week, 1, durationWeek ],\n [ month, 1, durationMonth ],\n [ month, 3, 3 * durationMonth ],\n [ year, 1, durationYear ]\n ];\n\n function tickFormat(date) {\n return (second(date) < date ? formatMillisecond\n : minute(date) < date ? formatSecond\n : hour(date) < date ? formatMinute\n : day(date) < date ? formatHour\n : month(date) < date ? (week(date) < date ? formatDay : formatWeek)\n : year(date) < date ? formatMonth\n : formatYear)(date);\n }\n\n function tickInterval(interval, start, stop) {\n if (interval == null) interval = 10;\n\n // If a desired tick count is specified, pick a reasonable tick interval\n // based on the extent of the domain and a rough estimate of tick size.\n // Otherwise, assume interval is already a time interval and use it.\n if (typeof interval === \"number\") {\n var target = Math.abs(stop - start) / interval,\n i = bisector(function(i) { return i[2]; }).right(tickIntervals, target),\n step;\n if (i === tickIntervals.length) {\n step = tickStep(start / durationYear, stop / durationYear, interval);\n interval = year;\n } else if (i) {\n i = tickIntervals[target / tickIntervals[i - 1][2] < tickIntervals[i][2] / target ? i - 1 : i];\n step = i[1];\n interval = i[0];\n } else {\n step = Math.max(tickStep(start, stop, interval), 1);\n interval = millisecond;\n }\n return interval.every(step);\n }\n\n return interval;\n }\n\n scale.invert = function(y) {\n return new Date(invert(y));\n };\n\n scale.domain = function(_) {\n return arguments.length ? domain(Array.from(_, number)) : domain().map(date);\n };\n\n scale.ticks = function(interval) {\n var d = domain(),\n t0 = d[0],\n t1 = d[d.length - 1],\n r = t1 < t0,\n t;\n if (r) t = t0, t0 = t1, t1 = t;\n t = tickInterval(interval, t0, t1);\n t = t ? t.range(t0, t1 + 1) : []; // inclusive stop\n return r ? t.reverse() : t;\n };\n\n scale.tickFormat = function(count, specifier) {\n return specifier == null ? tickFormat : format(specifier);\n };\n\n scale.nice = function(interval) {\n var d = domain();\n return (interval = tickInterval(interval, d[0], d[d.length - 1]))\n ? domain(nice(d, interval))\n : scale;\n };\n\n scale.copy = function() {\n return copy(scale, calendar(year, month, week, day, hour, minute, second, millisecond, format));\n };\n\n return scale;\n}\n\nexport default function() {\n return initRange.apply(calendar(timeYear, timeMonth, timeWeek, timeDay, timeHour, timeMinute, timeSecond, timeMillisecond, timeFormat).domain([new Date(2000, 0, 1), new Date(2000, 0, 2)]), arguments);\n}\n","import {calendar} from \"./time.js\";\nimport {utcFormat} from \"d3-time-format\";\nimport {utcYear, utcMonth, utcWeek, utcDay, utcHour, utcMinute, utcSecond, utcMillisecond} from \"d3-time\";\nimport {initRange} from \"./init.js\";\n\nexport default function() {\n return initRange.apply(calendar(utcYear, utcMonth, utcWeek, utcDay, utcHour, utcMinute, utcSecond, utcMillisecond, utcFormat).domain([Date.UTC(2000, 0, 1), Date.UTC(2000, 0, 2)]), arguments);\n}\n","import { ScaleOrdinal } from \"d3-scale\";\n\n/* The base class for all charts.\n *\n * Provides the following methods:\n *\n * - setHeight(num): set the height of the chart, accounting for margins.\n * - setWidth(num): set the width of the chart, accounting for margins.\n * - set(property, value): set the given property of the chart class to value.\n *\n * Charts should implement the following methods:\n *\n * - constructor(svg): Initialise the chart, prepare for drawing it to the\n * given (which is a d3-selection).\n * - draw(data): Draw the chart for the given data.\n * - update(): Update the chart (after resize, toggling, etc)\n */\nexport abstract class BaseChart {\n svg: SVGElement;\n\n margin: { top: number; right: number; left: number; bottom: number };\n\n height: number;\n\n outerHeight: number;\n\n width: number;\n\n outerWidth: number;\n\n legend?: {\n domain: string[];\n scale: ScaleOrdinal;\n };\n\n has_currency_setting?: boolean;\n\n has_mode_setting?: boolean;\n\n constructor(svg: SVGElement) {\n svg.setAttribute(\"class\", \"\");\n svg.innerHTML = \"\";\n this.svg = svg;\n this.margin = {\n top: 10,\n right: 10,\n bottom: 30,\n left: 40,\n };\n this.outerHeight = 300;\n this.height = this.outerHeight - this.margin.top - this.margin.bottom;\n this.outerWidth = 500;\n this.width = this.outerWidth - this.margin.left - this.margin.right;\n }\n\n abstract draw(data: unknown): this;\n\n setHeight(d: number) {\n this.svg.setAttribute(\"height\", `${d}`);\n this.outerHeight = d;\n this.height = d - this.margin.top - this.margin.bottom;\n return this;\n }\n\n setWidth(d: number) {\n this.svg.setAttribute(\"width\", `${d}`);\n this.outerWidth = d;\n this.width = d - this.margin.left - this.margin.right;\n return this;\n }\n\n set(property: T, value: this[T]) {\n this[property] = value;\n return this;\n }\n}\n","import { hcl } from \"d3-color\";\nimport { scaleOrdinal } from \"d3-scale\";\nimport { get } from \"svelte/store\";\n\nimport { filters } from \"../stores\";\nimport { currentTimeFilterDateFormat } from \"../format\";\n\nexport const NO_MARGINS = {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n};\n\nexport function setTimeFilter(date: Date) {\n filters.update(fs => ({\n ...fs,\n time: get(currentTimeFilterDateFormat)(date),\n }));\n}\n\n/*\n * Generate an array of colors.\n *\n * Uses the HCL color space in an attempt to generate colours that are\n * to be perceived to be of the same brightness.\n */\nfunction hclColorRange(count: number, chroma = 45, lightness = 70) {\n const offset = 270;\n const delta = 360 / count;\n const colors = [...Array(count).keys()].map(index => {\n const hue = (index * delta + offset) % 360;\n return hcl(hue, chroma, lightness);\n });\n return colors;\n}\n\nconst colors10 = hclColorRange(10).map(c => c.toString());\nconst colors15 = hclColorRange(15, 30, 80).map(c => c.toString());\n\n/*\n * The color scales for the charts.\n *\n * The scales for treemap and sunburst charts will be initialised with all\n * accounts on page init and currencies with all commodities.\n */\nexport const scales = {\n treemap: scaleOrdinal(colors15),\n sunburst: scaleOrdinal(colors10),\n currencies: scaleOrdinal(colors10),\n scatterplot: scaleOrdinal(colors10),\n};\n","import { event, select, Selection } from \"d3-selection\";\n\nimport e from \"../events\";\n\nexport const tooltip = select(document.body)\n .append(\"div\")\n .attr(\"class\", \"tooltip\");\n\n// Add a tooltip to the given selection.\nexport function addTooltip(\n selection: Selection,\n tooltipText: (d: T) => string\n) {\n selection\n .on(\"mouseenter\", (d: T) => {\n tooltip.style(\"opacity\", 1).html(tooltipText(d));\n })\n .on(\"mousemove\", () => {\n tooltip\n .style(\"left\", `${event.pageX}px`)\n .style(\"top\", `${event.pageY - 15}px`);\n })\n .on(\"mouseleave\", () => {\n tooltip.style(\"opacity\", 0);\n });\n}\n\ne.on(\"page-loaded\", () => {\n tooltip.style(\"opacity\", 0);\n});\n","import { max, min } from \"d3-array\";\nimport { axisLeft, axisBottom, Axis } from \"d3-axis\";\nimport { scaleBand, scaleLinear, ScaleLinear, ScaleBand } from \"d3-scale\";\nimport { select, Selection } from \"d3-selection\";\n\nimport { formatCurrencyShort } from \"../format\";\n\nimport { BaseChart } from \"./base\";\nimport { scales, setTimeFilter } from \"./helpers\";\nimport { addTooltip } from \"./tooltip\";\n\nconst maxColumnWidth = 100;\n\ninterface BarChartDatumValue {\n name: string;\n value: number;\n budget: number;\n}\n\ninterface BarChartDatum {\n label: string;\n date: Date;\n values: BarChartDatumValue[];\n}\n\nexport class BarChart extends BaseChart {\n canvas: Selection;\n\n x0: ScaleBand;\n\n x1: ScaleBand;\n\n y: ScaleLinear;\n\n xAxis: Axis;\n\n xAxisSelection: Selection;\n\n yAxis: Axis;\n\n yAxisSelection: Selection;\n\n groups: Selection;\n\n groupboxes: Selection;\n\n axisgroupboxes: Selection;\n\n bars: Selection;\n\n budgets: Selection;\n\n tooltipText?: (d: BarChartDatum) => string;\n\n constructor(svg: SVGElement) {\n super(svg);\n\n this.x0 = scaleBand().padding(0.1);\n this.x1 = scaleBand();\n this.y = scaleLinear();\n\n this.xAxis = axisBottom(this.x0).tickSizeOuter(0);\n this.yAxis = axisLeft(this.y).tickFormat(formatCurrencyShort);\n\n this.svg.setAttribute(\"class\", \"barchart\");\n this.canvas = select(this.svg)\n .classed(\"barchart\", true)\n .append(\"g\");\n this.xAxisSelection = this.canvas.append(\"g\").attr(\"class\", \"x axis\");\n this.yAxisSelection = this.canvas.append(\"g\").attr(\"class\", \"y axis\");\n\n this.groups = this.canvas.selectAll(\".group\");\n this.groupboxes = this.groups.append(\"rect\");\n this.axisgroupboxes = this.groups.append(\"rect\");\n this.bars = this.groups.selectAll(\".bar\");\n this.budgets = this.groups.selectAll(\".budget\");\n }\n\n draw(data: BarChartDatum[]) {\n this.x0.domain(data.map(d => d.label));\n this.x1.domain(data[0].values.map(d => d.name));\n\n this.y.domain([\n Math.min(0, min(data, d => min(d.values, x => x.value)) || 0),\n Math.max(0, max(data, d => max(d.values, x => x.value)) || 0),\n ]);\n\n this.groups = this.canvas\n .selectAll(\".group\")\n .data(data)\n .enter()\n .append(\"g\")\n .attr(\"class\", \"group\")\n .call(addTooltip, this.tooltipText);\n\n this.groupboxes = this.groups.append(\"rect\").attr(\"class\", \"group-box\");\n\n this.axisgroupboxes = this.groups\n .append(\"rect\")\n .on(\"click\", d => {\n setTimeFilter(d.date);\n })\n .attr(\"class\", \"axis-group-box\");\n\n this.bars = this.groups\n .selectAll(\".bar\")\n .data(d => d.values)\n .enter()\n .append(\"rect\")\n .attr(\"class\", \"bar\")\n .style(\"fill\", d => scales.currencies(d.name));\n\n this.budgets = this.groups\n .selectAll(\".budget\")\n .data(d => d.values)\n .enter()\n .append(\"rect\")\n .attr(\"class\", \"budget\");\n\n this.update();\n return this;\n }\n\n update() {\n const screenWidth = this.width;\n const maxWidth = this.groups.size() * maxColumnWidth;\n const offset = this.margin.left + Math.max(0, screenWidth - maxWidth) / 2;\n\n this.width = Math.min(screenWidth, maxWidth);\n this.setHeight(250);\n\n this.y.range([this.height, 0]);\n this.x0.range([0, this.width]);\n this.x1.range([0, this.x0.bandwidth()]);\n\n this.canvas.attr(\"transform\", `translate(${offset},${this.margin.top})`);\n\n this.yAxis.tickSize(-this.width);\n this.xAxisSelection.attr(\"transform\", `translate(0,${this.height})`);\n\n this.xAxis.tickValues(this.filterTicks(this.x0.domain()));\n this.xAxisSelection.call(this.xAxis);\n this.yAxisSelection.call(this.yAxis);\n\n this.groups.attr(\"transform\", d => `translate(${this.x0(d.label)},0)`);\n\n this.groupboxes\n .attr(\"width\", this.x0.bandwidth())\n .attr(\"height\", this.height);\n\n this.axisgroupboxes\n .attr(\"width\", this.x0.bandwidth())\n .attr(\"height\", this.margin.bottom)\n .attr(\"transform\", `translate(0,${this.height})`);\n\n this.budgets\n .attr(\"width\", this.x1.bandwidth())\n .attr(\"x\", d => this.x1(d.name) || 0)\n .attr(\"y\", d => this.y(Math.max(0, d.budget)))\n .attr(\"height\", d => Math.abs(this.y(d.budget) - this.y(0)));\n\n this.bars\n .attr(\"width\", this.x1.bandwidth())\n .attr(\"x\", d => this.x1(d.name) || 0)\n .attr(\"y\", d => this.y(Math.max(0, d.value)))\n .attr(\"height\", d => Math.abs(this.y(d.value) - this.y(0)));\n\n this.legend = {\n domain: this.x1.domain(),\n scale: scales.currencies,\n };\n }\n\n filterTicks(domain: string[]) {\n const labelsCount = this.width / 70;\n if (domain.length <= labelsCount) {\n return domain;\n }\n const showIndices = Math.ceil(domain.length / labelsCount);\n return domain.filter((d, i) => i % showIndices === 0);\n }\n}\n","var pi = Math.PI,\n tau = 2 * pi,\n epsilon = 1e-6,\n tauEpsilon = tau - epsilon;\n\nfunction Path() {\n this._x0 = this._y0 = // start of current subpath\n this._x1 = this._y1 = null; // end of current subpath\n this._ = \"\";\n}\n\nfunction path() {\n return new Path;\n}\n\nPath.prototype = path.prototype = {\n constructor: Path,\n moveTo: function(x, y) {\n this._ += \"M\" + (this._x0 = this._x1 = +x) + \",\" + (this._y0 = this._y1 = +y);\n },\n closePath: function() {\n if (this._x1 !== null) {\n this._x1 = this._x0, this._y1 = this._y0;\n this._ += \"Z\";\n }\n },\n lineTo: function(x, y) {\n this._ += \"L\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n },\n quadraticCurveTo: function(x1, y1, x, y) {\n this._ += \"Q\" + (+x1) + \",\" + (+y1) + \",\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n },\n bezierCurveTo: function(x1, y1, x2, y2, x, y) {\n this._ += \"C\" + (+x1) + \",\" + (+y1) + \",\" + (+x2) + \",\" + (+y2) + \",\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n },\n arcTo: function(x1, y1, x2, y2, r) {\n x1 = +x1, y1 = +y1, x2 = +x2, y2 = +y2, r = +r;\n var x0 = this._x1,\n y0 = this._y1,\n x21 = x2 - x1,\n y21 = y2 - y1,\n x01 = x0 - x1,\n y01 = y0 - y1,\n l01_2 = x01 * x01 + y01 * y01;\n\n // Is the radius negative? Error.\n if (r < 0) throw new Error(\"negative radius: \" + r);\n\n // Is this path empty? Move to (x1,y1).\n if (this._x1 === null) {\n this._ += \"M\" + (this._x1 = x1) + \",\" + (this._y1 = y1);\n }\n\n // Or, is (x1,y1) coincident with (x0,y0)? Do nothing.\n else if (!(l01_2 > epsilon));\n\n // Or, are (x0,y0), (x1,y1) and (x2,y2) collinear?\n // Equivalently, is (x1,y1) coincident with (x2,y2)?\n // Or, is the radius zero? Line to (x1,y1).\n else if (!(Math.abs(y01 * x21 - y21 * x01) > epsilon) || !r) {\n this._ += \"L\" + (this._x1 = x1) + \",\" + (this._y1 = y1);\n }\n\n // Otherwise, draw an arc!\n else {\n var x20 = x2 - x0,\n y20 = y2 - y0,\n l21_2 = x21 * x21 + y21 * y21,\n l20_2 = x20 * x20 + y20 * y20,\n l21 = Math.sqrt(l21_2),\n l01 = Math.sqrt(l01_2),\n l = r * Math.tan((pi - Math.acos((l21_2 + l01_2 - l20_2) / (2 * l21 * l01))) / 2),\n t01 = l / l01,\n t21 = l / l21;\n\n // If the start tangent is not coincident with (x0,y0), line to.\n if (Math.abs(t01 - 1) > epsilon) {\n this._ += \"L\" + (x1 + t01 * x01) + \",\" + (y1 + t01 * y01);\n }\n\n this._ += \"A\" + r + \",\" + r + \",0,0,\" + (+(y01 * x20 > x01 * y20)) + \",\" + (this._x1 = x1 + t21 * x21) + \",\" + (this._y1 = y1 + t21 * y21);\n }\n },\n arc: function(x, y, r, a0, a1, ccw) {\n x = +x, y = +y, r = +r, ccw = !!ccw;\n var dx = r * Math.cos(a0),\n dy = r * Math.sin(a0),\n x0 = x + dx,\n y0 = y + dy,\n cw = 1 ^ ccw,\n da = ccw ? a0 - a1 : a1 - a0;\n\n // Is the radius negative? Error.\n if (r < 0) throw new Error(\"negative radius: \" + r);\n\n // Is this path empty? Move to (x0,y0).\n if (this._x1 === null) {\n this._ += \"M\" + x0 + \",\" + y0;\n }\n\n // Or, is (x0,y0) not coincident with the previous point? Line to (x0,y0).\n else if (Math.abs(this._x1 - x0) > epsilon || Math.abs(this._y1 - y0) > epsilon) {\n this._ += \"L\" + x0 + \",\" + y0;\n }\n\n // Is this arc empty? We’re done.\n if (!r) return;\n\n // Does the angle go the wrong way? Flip the direction.\n if (da < 0) da = da % tau + tau;\n\n // Is this a complete circle? Draw two arcs to complete the circle.\n if (da > tauEpsilon) {\n this._ += \"A\" + r + \",\" + r + \",0,1,\" + cw + \",\" + (x - dx) + \",\" + (y - dy) + \"A\" + r + \",\" + r + \",0,1,\" + cw + \",\" + (this._x1 = x0) + \",\" + (this._y1 = y0);\n }\n\n // Is this arc non-empty? Draw an arc!\n else if (da > epsilon) {\n this._ += \"A\" + r + \",\" + r + \",0,\" + (+(da >= pi)) + \",\" + cw + \",\" + (this._x1 = x + r * Math.cos(a1)) + \",\" + (this._y1 = y + r * Math.sin(a1));\n }\n },\n rect: function(x, y, w, h) {\n this._ += \"M\" + (this._x0 = this._x1 = +x) + \",\" + (this._y0 = this._y1 = +y) + \"h\" + (+w) + \"v\" + (+h) + \"h\" + (-w) + \"Z\";\n },\n toString: function() {\n return this._;\n }\n};\n\nexport default path;\n","export default function(x) {\n return function constant() {\n return x;\n };\n}\n","export var abs = Math.abs;\nexport var atan2 = Math.atan2;\nexport var cos = Math.cos;\nexport var max = Math.max;\nexport var min = Math.min;\nexport var sin = Math.sin;\nexport var sqrt = Math.sqrt;\n\nexport var epsilon = 1e-12;\nexport var pi = Math.PI;\nexport var halfPi = pi / 2;\nexport var tau = 2 * pi;\n\nexport function acos(x) {\n return x > 1 ? 0 : x < -1 ? pi : Math.acos(x);\n}\n\nexport function asin(x) {\n return x >= 1 ? halfPi : x <= -1 ? -halfPi : Math.asin(x);\n}\n","import {path} from \"d3-path\";\nimport constant from \"./constant.js\";\nimport {abs, acos, asin, atan2, cos, epsilon, halfPi, max, min, pi, sin, sqrt, tau} from \"./math.js\";\n\nfunction arcInnerRadius(d) {\n return d.innerRadius;\n}\n\nfunction arcOuterRadius(d) {\n return d.outerRadius;\n}\n\nfunction arcStartAngle(d) {\n return d.startAngle;\n}\n\nfunction arcEndAngle(d) {\n return d.endAngle;\n}\n\nfunction arcPadAngle(d) {\n return d && d.padAngle; // Note: optional!\n}\n\nfunction intersect(x0, y0, x1, y1, x2, y2, x3, y3) {\n var x10 = x1 - x0, y10 = y1 - y0,\n x32 = x3 - x2, y32 = y3 - y2,\n t = y32 * x10 - x32 * y10;\n if (t * t < epsilon) return;\n t = (x32 * (y0 - y2) - y32 * (x0 - x2)) / t;\n return [x0 + t * x10, y0 + t * y10];\n}\n\n// Compute perpendicular offset line of length rc.\n// http://mathworld.wolfram.com/Circle-LineIntersection.html\nfunction cornerTangents(x0, y0, x1, y1, r1, rc, cw) {\n var x01 = x0 - x1,\n y01 = y0 - y1,\n lo = (cw ? rc : -rc) / sqrt(x01 * x01 + y01 * y01),\n ox = lo * y01,\n oy = -lo * x01,\n x11 = x0 + ox,\n y11 = y0 + oy,\n x10 = x1 + ox,\n y10 = y1 + oy,\n x00 = (x11 + x10) / 2,\n y00 = (y11 + y10) / 2,\n dx = x10 - x11,\n dy = y10 - y11,\n d2 = dx * dx + dy * dy,\n r = r1 - rc,\n D = x11 * y10 - x10 * y11,\n d = (dy < 0 ? -1 : 1) * sqrt(max(0, r * r * d2 - D * D)),\n cx0 = (D * dy - dx * d) / d2,\n cy0 = (-D * dx - dy * d) / d2,\n cx1 = (D * dy + dx * d) / d2,\n cy1 = (-D * dx + dy * d) / d2,\n dx0 = cx0 - x00,\n dy0 = cy0 - y00,\n dx1 = cx1 - x00,\n dy1 = cy1 - y00;\n\n // Pick the closer of the two intersection points.\n // TODO Is there a faster way to determine which intersection to use?\n if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1;\n\n return {\n cx: cx0,\n cy: cy0,\n x01: -ox,\n y01: -oy,\n x11: cx0 * (r1 / r - 1),\n y11: cy0 * (r1 / r - 1)\n };\n}\n\nexport default function() {\n var innerRadius = arcInnerRadius,\n outerRadius = arcOuterRadius,\n cornerRadius = constant(0),\n padRadius = null,\n startAngle = arcStartAngle,\n endAngle = arcEndAngle,\n padAngle = arcPadAngle,\n context = null;\n\n function arc() {\n var buffer,\n r,\n r0 = +innerRadius.apply(this, arguments),\n r1 = +outerRadius.apply(this, arguments),\n a0 = startAngle.apply(this, arguments) - halfPi,\n a1 = endAngle.apply(this, arguments) - halfPi,\n da = abs(a1 - a0),\n cw = a1 > a0;\n\n if (!context) context = buffer = path();\n\n // Ensure that the outer radius is always larger than the inner radius.\n if (r1 < r0) r = r1, r1 = r0, r0 = r;\n\n // Is it a point?\n if (!(r1 > epsilon)) context.moveTo(0, 0);\n\n // Or is it a circle or annulus?\n else if (da > tau - epsilon) {\n context.moveTo(r1 * cos(a0), r1 * sin(a0));\n context.arc(0, 0, r1, a0, a1, !cw);\n if (r0 > epsilon) {\n context.moveTo(r0 * cos(a1), r0 * sin(a1));\n context.arc(0, 0, r0, a1, a0, cw);\n }\n }\n\n // Or is it a circular or annular sector?\n else {\n var a01 = a0,\n a11 = a1,\n a00 = a0,\n a10 = a1,\n da0 = da,\n da1 = da,\n ap = padAngle.apply(this, arguments) / 2,\n rp = (ap > epsilon) && (padRadius ? +padRadius.apply(this, arguments) : sqrt(r0 * r0 + r1 * r1)),\n rc = min(abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments)),\n rc0 = rc,\n rc1 = rc,\n t0,\n t1;\n\n // Apply padding? Note that since r1 ≥ r0, da1 ≥ da0.\n if (rp > epsilon) {\n var p0 = asin(rp / r0 * sin(ap)),\n p1 = asin(rp / r1 * sin(ap));\n if ((da0 -= p0 * 2) > epsilon) p0 *= (cw ? 1 : -1), a00 += p0, a10 -= p0;\n else da0 = 0, a00 = a10 = (a0 + a1) / 2;\n if ((da1 -= p1 * 2) > epsilon) p1 *= (cw ? 1 : -1), a01 += p1, a11 -= p1;\n else da1 = 0, a01 = a11 = (a0 + a1) / 2;\n }\n\n var x01 = r1 * cos(a01),\n y01 = r1 * sin(a01),\n x10 = r0 * cos(a10),\n y10 = r0 * sin(a10);\n\n // Apply rounded corners?\n if (rc > epsilon) {\n var x11 = r1 * cos(a11),\n y11 = r1 * sin(a11),\n x00 = r0 * cos(a00),\n y00 = r0 * sin(a00),\n oc;\n\n // Restrict the corner radius according to the sector angle.\n if (da < pi && (oc = intersect(x01, y01, x00, y00, x11, y11, x10, y10))) {\n var ax = x01 - oc[0],\n ay = y01 - oc[1],\n bx = x11 - oc[0],\n by = y11 - oc[1],\n kc = 1 / sin(acos((ax * bx + ay * by) / (sqrt(ax * ax + ay * ay) * sqrt(bx * bx + by * by))) / 2),\n lc = sqrt(oc[0] * oc[0] + oc[1] * oc[1]);\n rc0 = min(rc, (r0 - lc) / (kc - 1));\n rc1 = min(rc, (r1 - lc) / (kc + 1));\n }\n }\n\n // Is the sector collapsed to a line?\n if (!(da1 > epsilon)) context.moveTo(x01, y01);\n\n // Does the sector’s outer ring have rounded corners?\n else if (rc1 > epsilon) {\n t0 = cornerTangents(x00, y00, x01, y01, r1, rc1, cw);\n t1 = cornerTangents(x11, y11, x10, y10, r1, rc1, cw);\n\n context.moveTo(t0.cx + t0.x01, t0.cy + t0.y01);\n\n // Have the corners merged?\n if (rc1 < rc) context.arc(t0.cx, t0.cy, rc1, atan2(t0.y01, t0.x01), atan2(t1.y01, t1.x01), !cw);\n\n // Otherwise, draw the two corners and the ring.\n else {\n context.arc(t0.cx, t0.cy, rc1, atan2(t0.y01, t0.x01), atan2(t0.y11, t0.x11), !cw);\n context.arc(0, 0, r1, atan2(t0.cy + t0.y11, t0.cx + t0.x11), atan2(t1.cy + t1.y11, t1.cx + t1.x11), !cw);\n context.arc(t1.cx, t1.cy, rc1, atan2(t1.y11, t1.x11), atan2(t1.y01, t1.x01), !cw);\n }\n }\n\n // Or is the outer ring just a circular arc?\n else context.moveTo(x01, y01), context.arc(0, 0, r1, a01, a11, !cw);\n\n // Is there no inner ring, and it’s a circular sector?\n // Or perhaps it’s an annular sector collapsed due to padding?\n if (!(r0 > epsilon) || !(da0 > epsilon)) context.lineTo(x10, y10);\n\n // Does the sector’s inner ring (or point) have rounded corners?\n else if (rc0 > epsilon) {\n t0 = cornerTangents(x10, y10, x11, y11, r0, -rc0, cw);\n t1 = cornerTangents(x01, y01, x00, y00, r0, -rc0, cw);\n\n context.lineTo(t0.cx + t0.x01, t0.cy + t0.y01);\n\n // Have the corners merged?\n if (rc0 < rc) context.arc(t0.cx, t0.cy, rc0, atan2(t0.y01, t0.x01), atan2(t1.y01, t1.x01), !cw);\n\n // Otherwise, draw the two corners and the ring.\n else {\n context.arc(t0.cx, t0.cy, rc0, atan2(t0.y01, t0.x01), atan2(t0.y11, t0.x11), !cw);\n context.arc(0, 0, r0, atan2(t0.cy + t0.y11, t0.cx + t0.x11), atan2(t1.cy + t1.y11, t1.cx + t1.x11), cw);\n context.arc(t1.cx, t1.cy, rc0, atan2(t1.y11, t1.x11), atan2(t1.y01, t1.x01), !cw);\n }\n }\n\n // Or is the inner ring just a circular arc?\n else context.arc(0, 0, r0, a10, a00, cw);\n }\n\n context.closePath();\n\n if (buffer) return context = null, buffer + \"\" || null;\n }\n\n arc.centroid = function() {\n var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2,\n a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - pi / 2;\n return [cos(a) * r, sin(a) * r];\n };\n\n arc.innerRadius = function(_) {\n return arguments.length ? (innerRadius = typeof _ === \"function\" ? _ : constant(+_), arc) : innerRadius;\n };\n\n arc.outerRadius = function(_) {\n return arguments.length ? (outerRadius = typeof _ === \"function\" ? _ : constant(+_), arc) : outerRadius;\n };\n\n arc.cornerRadius = function(_) {\n return arguments.length ? (cornerRadius = typeof _ === \"function\" ? _ : constant(+_), arc) : cornerRadius;\n };\n\n arc.padRadius = function(_) {\n return arguments.length ? (padRadius = _ == null ? null : typeof _ === \"function\" ? _ : constant(+_), arc) : padRadius;\n };\n\n arc.startAngle = function(_) {\n return arguments.length ? (startAngle = typeof _ === \"function\" ? _ : constant(+_), arc) : startAngle;\n };\n\n arc.endAngle = function(_) {\n return arguments.length ? (endAngle = typeof _ === \"function\" ? _ : constant(+_), arc) : endAngle;\n };\n\n arc.padAngle = function(_) {\n return arguments.length ? (padAngle = typeof _ === \"function\" ? _ : constant(+_), arc) : padAngle;\n };\n\n arc.context = function(_) {\n return arguments.length ? ((context = _ == null ? null : _), arc) : context;\n };\n\n return arc;\n}\n","function Linear(context) {\n this._context = context;\n}\n\nLinear.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._point = 0;\n },\n lineEnd: function() {\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; // proceed\n default: this._context.lineTo(x, y); break;\n }\n }\n};\n\nexport default function(context) {\n return new Linear(context);\n}\n","export function x(p) {\n return p[0];\n}\n\nexport function y(p) {\n return p[1];\n}\n","import {path} from \"d3-path\";\nimport constant from \"./constant.js\";\nimport curveLinear from \"./curve/linear.js\";\nimport {x as pointX, y as pointY} from \"./point.js\";\n\nexport default function() {\n var x = pointX,\n y = pointY,\n defined = constant(true),\n context = null,\n curve = curveLinear,\n output = null;\n\n function line(data) {\n var i,\n n = data.length,\n d,\n defined0 = false,\n buffer;\n\n if (context == null) output = curve(buffer = path());\n\n for (i = 0; i <= n; ++i) {\n if (!(i < n && defined(d = data[i], i, data)) === defined0) {\n if (defined0 = !defined0) output.lineStart();\n else output.lineEnd();\n }\n if (defined0) output.point(+x(d, i, data), +y(d, i, data));\n }\n\n if (buffer) return output = null, buffer + \"\" || null;\n }\n\n line.x = function(_) {\n return arguments.length ? (x = typeof _ === \"function\" ? _ : constant(+_), line) : x;\n };\n\n line.y = function(_) {\n return arguments.length ? (y = typeof _ === \"function\" ? _ : constant(+_), line) : y;\n };\n\n line.defined = function(_) {\n return arguments.length ? (defined = typeof _ === \"function\" ? _ : constant(!!_), line) : defined;\n };\n\n line.curve = function(_) {\n return arguments.length ? (curve = _, context != null && (output = curve(context)), line) : curve;\n };\n\n line.context = function(_) {\n return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), line) : context;\n };\n\n return line;\n}\n","export default function(d) {\n var x = +this._x.call(null, d),\n y = +this._y.call(null, d);\n return add(this.cover(x, y), x, y, d);\n}\n\nfunction add(tree, x, y, d) {\n if (isNaN(x) || isNaN(y)) return tree; // ignore invalid points\n\n var parent,\n node = tree._root,\n leaf = {data: d},\n x0 = tree._x0,\n y0 = tree._y0,\n x1 = tree._x1,\n y1 = tree._y1,\n xm,\n ym,\n xp,\n yp,\n right,\n bottom,\n i,\n j;\n\n // If the tree is empty, initialize the root as a leaf.\n if (!node) return tree._root = leaf, tree;\n\n // Find the existing leaf for the new point, or add it.\n while (node.length) {\n if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;\n if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;\n if (parent = node, !(node = node[i = bottom << 1 | right])) return parent[i] = leaf, tree;\n }\n\n // Is the new point is exactly coincident with the existing point?\n xp = +tree._x.call(null, node.data);\n yp = +tree._y.call(null, node.data);\n if (x === xp && y === yp) return leaf.next = node, parent ? parent[i] = leaf : tree._root = leaf, tree;\n\n // Otherwise, split the leaf node until the old and new point are separated.\n do {\n parent = parent ? parent[i] = new Array(4) : tree._root = new Array(4);\n if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;\n if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;\n } while ((i = bottom << 1 | right) === (j = (yp >= ym) << 1 | (xp >= xm)));\n return parent[j] = node, parent[i] = leaf, tree;\n}\n\nexport function addAll(data) {\n var d, i, n = data.length,\n x,\n y,\n xz = new Array(n),\n yz = new Array(n),\n x0 = Infinity,\n y0 = Infinity,\n x1 = -Infinity,\n y1 = -Infinity;\n\n // Compute the points and their extent.\n for (i = 0; i < n; ++i) {\n if (isNaN(x = +this._x.call(null, d = data[i])) || isNaN(y = +this._y.call(null, d))) continue;\n xz[i] = x;\n yz[i] = y;\n if (x < x0) x0 = x;\n if (x > x1) x1 = x;\n if (y < y0) y0 = y;\n if (y > y1) y1 = y;\n }\n\n // If there were no (valid) points, abort.\n if (x0 > x1 || y0 > y1) return this;\n\n // Expand the tree to cover the new points.\n this.cover(x0, y0).cover(x1, y1);\n\n // Add the new points.\n for (i = 0; i < n; ++i) {\n add(this, xz[i], yz[i], data[i]);\n }\n\n return this;\n}\n","export default function(x, y) {\n if (isNaN(x = +x) || isNaN(y = +y)) return this; // ignore invalid points\n\n var x0 = this._x0,\n y0 = this._y0,\n x1 = this._x1,\n y1 = this._y1;\n\n // If the quadtree has no extent, initialize them.\n // Integer extent are necessary so that if we later double the extent,\n // the existing quadrant boundaries don’t change due to floating point error!\n if (isNaN(x0)) {\n x1 = (x0 = Math.floor(x)) + 1;\n y1 = (y0 = Math.floor(y)) + 1;\n }\n\n // Otherwise, double repeatedly to cover.\n else {\n var z = x1 - x0,\n node = this._root,\n parent,\n i;\n\n while (x0 > x || x >= x1 || y0 > y || y >= y1) {\n i = (y < y0) << 1 | (x < x0);\n parent = new Array(4), parent[i] = node, node = parent, z *= 2;\n switch (i) {\n case 0: x1 = x0 + z, y1 = y0 + z; break;\n case 1: x0 = x1 - z, y1 = y0 + z; break;\n case 2: x1 = x0 + z, y0 = y1 - z; break;\n case 3: x0 = x1 - z, y0 = y1 - z; break;\n }\n }\n\n if (this._root && this._root.length) this._root = node;\n }\n\n this._x0 = x0;\n this._y0 = y0;\n this._x1 = x1;\n this._y1 = y1;\n return this;\n}\n","export default function() {\n var data = [];\n this.visit(function(node) {\n if (!node.length) do data.push(node.data); while (node = node.next)\n });\n return data;\n}\n","export default function(_) {\n return arguments.length\n ? this.cover(+_[0][0], +_[0][1]).cover(+_[1][0], +_[1][1])\n : isNaN(this._x0) ? undefined : [[this._x0, this._y0], [this._x1, this._y1]];\n}\n","export default function(node, x0, y0, x1, y1) {\n this.node = node;\n this.x0 = x0;\n this.y0 = y0;\n this.x1 = x1;\n this.y1 = y1;\n}\n","import Quad from \"./quad.js\";\n\nexport default function(x, y, radius) {\n var data,\n x0 = this._x0,\n y0 = this._y0,\n x1,\n y1,\n x2,\n y2,\n x3 = this._x1,\n y3 = this._y1,\n quads = [],\n node = this._root,\n q,\n i;\n\n if (node) quads.push(new Quad(node, x0, y0, x3, y3));\n if (radius == null) radius = Infinity;\n else {\n x0 = x - radius, y0 = y - radius;\n x3 = x + radius, y3 = y + radius;\n radius *= radius;\n }\n\n while (q = quads.pop()) {\n\n // Stop searching if this quadrant can’t contain a closer node.\n if (!(node = q.node)\n || (x1 = q.x0) > x3\n || (y1 = q.y0) > y3\n || (x2 = q.x1) < x0\n || (y2 = q.y1) < y0) continue;\n\n // Bisect the current quadrant.\n if (node.length) {\n var xm = (x1 + x2) / 2,\n ym = (y1 + y2) / 2;\n\n quads.push(\n new Quad(node[3], xm, ym, x2, y2),\n new Quad(node[2], x1, ym, xm, y2),\n new Quad(node[1], xm, y1, x2, ym),\n new Quad(node[0], x1, y1, xm, ym)\n );\n\n // Visit the closest quadrant first.\n if (i = (y >= ym) << 1 | (x >= xm)) {\n q = quads[quads.length - 1];\n quads[quads.length - 1] = quads[quads.length - 1 - i];\n quads[quads.length - 1 - i] = q;\n }\n }\n\n // Visit this point. (Visiting coincident points isn’t necessary!)\n else {\n var dx = x - +this._x.call(null, node.data),\n dy = y - +this._y.call(null, node.data),\n d2 = dx * dx + dy * dy;\n if (d2 < radius) {\n var d = Math.sqrt(radius = d2);\n x0 = x - d, y0 = y - d;\n x3 = x + d, y3 = y + d;\n data = node.data;\n }\n }\n }\n\n return data;\n}\n","export default function(d) {\n if (isNaN(x = +this._x.call(null, d)) || isNaN(y = +this._y.call(null, d))) return this; // ignore invalid points\n\n var parent,\n node = this._root,\n retainer,\n previous,\n next,\n x0 = this._x0,\n y0 = this._y0,\n x1 = this._x1,\n y1 = this._y1,\n x,\n y,\n xm,\n ym,\n right,\n bottom,\n i,\n j;\n\n // If the tree is empty, initialize the root as a leaf.\n if (!node) return this;\n\n // Find the leaf node for the point.\n // While descending, also retain the deepest parent with a non-removed sibling.\n if (node.length) while (true) {\n if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;\n if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;\n if (!(parent = node, node = node[i = bottom << 1 | right])) return this;\n if (!node.length) break;\n if (parent[(i + 1) & 3] || parent[(i + 2) & 3] || parent[(i + 3) & 3]) retainer = parent, j = i;\n }\n\n // Find the point to remove.\n while (node.data !== d) if (!(previous = node, node = node.next)) return this;\n if (next = node.next) delete node.next;\n\n // If there are multiple coincident points, remove just the point.\n if (previous) return (next ? previous.next = next : delete previous.next), this;\n\n // If this is the root point, remove it.\n if (!parent) return this._root = next, this;\n\n // Remove this leaf.\n next ? parent[i] = next : delete parent[i];\n\n // If the parent now contains exactly one leaf, collapse superfluous parents.\n if ((node = parent[0] || parent[1] || parent[2] || parent[3])\n && node === (parent[3] || parent[2] || parent[1] || parent[0])\n && !node.length) {\n if (retainer) retainer[j] = node;\n else this._root = node;\n }\n\n return this;\n}\n\nexport function removeAll(data) {\n for (var i = 0, n = data.length; i < n; ++i) this.remove(data[i]);\n return this;\n}\n","export default function() {\n return this._root;\n}\n","export default function() {\n var size = 0;\n this.visit(function(node) {\n if (!node.length) do ++size; while (node = node.next)\n });\n return size;\n}\n","import Quad from \"./quad.js\";\n\nexport default function(callback) {\n var quads = [], q, node = this._root, child, x0, y0, x1, y1;\n if (node) quads.push(new Quad(node, this._x0, this._y0, this._x1, this._y1));\n while (q = quads.pop()) {\n if (!callback(node = q.node, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1) && node.length) {\n var xm = (x0 + x1) / 2, ym = (y0 + y1) / 2;\n if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1));\n if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1));\n if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym));\n if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym));\n }\n }\n return this;\n}\n","import Quad from \"./quad.js\";\n\nexport default function(callback) {\n var quads = [], next = [], q;\n if (this._root) quads.push(new Quad(this._root, this._x0, this._y0, this._x1, this._y1));\n while (q = quads.pop()) {\n var node = q.node;\n if (node.length) {\n var child, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1, xm = (x0 + x1) / 2, ym = (y0 + y1) / 2;\n if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym));\n if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym));\n if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1));\n if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1));\n }\n next.push(q);\n }\n while (q = next.pop()) {\n callback(q.node, q.x0, q.y0, q.x1, q.y1);\n }\n return this;\n}\n","export function defaultX(d) {\n return d[0];\n}\n\nexport default function(_) {\n return arguments.length ? (this._x = _, this) : this._x;\n}\n","export function defaultY(d) {\n return d[1];\n}\n\nexport default function(_) {\n return arguments.length ? (this._y = _, this) : this._y;\n}\n","import tree_add, {addAll as tree_addAll} from \"./add.js\";\nimport tree_cover from \"./cover.js\";\nimport tree_data from \"./data.js\";\nimport tree_extent from \"./extent.js\";\nimport tree_find from \"./find.js\";\nimport tree_remove, {removeAll as tree_removeAll} from \"./remove.js\";\nimport tree_root from \"./root.js\";\nimport tree_size from \"./size.js\";\nimport tree_visit from \"./visit.js\";\nimport tree_visitAfter from \"./visitAfter.js\";\nimport tree_x, {defaultX} from \"./x.js\";\nimport tree_y, {defaultY} from \"./y.js\";\n\nexport default function quadtree(nodes, x, y) {\n var tree = new Quadtree(x == null ? defaultX : x, y == null ? defaultY : y, NaN, NaN, NaN, NaN);\n return nodes == null ? tree : tree.addAll(nodes);\n}\n\nfunction Quadtree(x, y, x0, y0, x1, y1) {\n this._x = x;\n this._y = y;\n this._x0 = x0;\n this._y0 = y0;\n this._x1 = x1;\n this._y1 = y1;\n this._root = undefined;\n}\n\nfunction leaf_copy(leaf) {\n var copy = {data: leaf.data}, next = copy;\n while (leaf = leaf.next) next = next.next = {data: leaf.data};\n return copy;\n}\n\nvar treeProto = quadtree.prototype = Quadtree.prototype;\n\ntreeProto.copy = function() {\n var copy = new Quadtree(this._x, this._y, this._x0, this._y0, this._x1, this._y1),\n node = this._root,\n nodes,\n child;\n\n if (!node) return copy;\n\n if (!node.length) return copy._root = leaf_copy(node), copy;\n\n nodes = [{source: node, target: copy._root = new Array(4)}];\n while (node = nodes.pop()) {\n for (var i = 0; i < 4; ++i) {\n if (child = node.source[i]) {\n if (child.length) nodes.push({source: child, target: node.target[i] = new Array(4)});\n else node.target[i] = leaf_copy(child);\n }\n }\n }\n\n return copy;\n};\n\ntreeProto.add = tree_add;\ntreeProto.addAll = tree_addAll;\ntreeProto.cover = tree_cover;\ntreeProto.data = tree_data;\ntreeProto.extent = tree_extent;\ntreeProto.find = tree_find;\ntreeProto.remove = tree_remove;\ntreeProto.removeAll = tree_removeAll;\ntreeProto.root = tree_root;\ntreeProto.size = tree_size;\ntreeProto.visit = tree_visit;\ntreeProto.visitAfter = tree_visitAfter;\ntreeProto.x = tree_x;\ntreeProto.y = tree_y;\n","import { max, merge, min } from \"d3-array\";\nimport { axisLeft, axisBottom, Axis } from \"d3-axis\";\nimport { scaleLinear, scaleUtc, ScaleLinear, ScaleTime } from \"d3-scale\";\nimport { event, clientPoint, select, Selection, BaseType } from \"d3-selection\";\nimport { line, Line } from \"d3-shape\";\nimport { quadtree, Quadtree } from \"d3-quadtree\";\n\nimport { formatCurrencyShort } from \"../format\";\n\nimport { BaseChart } from \"./base\";\nimport { scales } from \"./helpers\";\nimport { tooltip } from \"./tooltip\";\n\nexport interface LineChartDatum {\n name: string;\n date: Date;\n value: number;\n}\n\nexport type LineChartData = {\n name: string;\n values: LineChartDatum[];\n};\n\nexport class LineChart extends BaseChart {\n canvas: Selection;\n\n data: LineChartData[];\n\n x: ScaleTime;\n\n y: ScaleLinear;\n\n xAxis: Axis;\n\n xAxisSelection: Selection;\n\n yAxis: Axis;\n\n yAxisSelection: Selection;\n\n line: Line;\n\n tooltipText?: (d: LineChartDatum) => string;\n\n quadtree: Quadtree;\n\n lines: Selection;\n\n dots: Selection;\n\n constructor(svg: SVGElement) {\n super(svg);\n\n this.data = [];\n this.x = scaleUtc();\n this.y = scaleLinear();\n\n this.xAxis = axisBottom(this.x).tickSizeOuter(0);\n\n this.yAxis = axisLeft(this.y)\n .tickPadding(6)\n .tickFormat(formatCurrencyShort);\n\n this.line = line()\n .x(d => this.x(d.date))\n .y(d => this.y(d.value));\n\n this.canvas = select(this.svg)\n .classed(\"linechart\", true)\n .append(\"g\");\n this.xAxisSelection = this.canvas.append(\"g\").attr(\"class\", \"x axis\");\n this.yAxisSelection = this.canvas.append(\"g\").attr(\"class\", \"y axis\");\n this.quadtree = quadtree();\n this.lines = this.canvas.selectAll(\".line\");\n this.dots = this.canvas.selectAll(\"g.dot\").selectAll(\"circle\");\n }\n\n draw(data: LineChartData[]) {\n this.data = data;\n this.x.domain([\n min(this.data, s => s.values[0].date) || 0,\n max(this.data, s => s.values[s.values.length - 1].date) || 0,\n ]);\n\n // Span y-axis as max minus min value plus 5 percent margin\n const minDataValue = min(this.data, d => min(d.values, x => x.value));\n const maxDataValue = max(this.data, d => max(d.values, x => x.value));\n if (minDataValue !== undefined && maxDataValue !== undefined) {\n this.y.domain([\n minDataValue - (maxDataValue - minDataValue) * 0.05,\n maxDataValue + (maxDataValue - minDataValue) * 0.05,\n ]);\n }\n\n this.lines = this.canvas\n .selectAll(\".line\")\n .data(data)\n .enter()\n .append(\"path\")\n .attr(\"class\", \"line\")\n .style(\"stroke\", d => scales.currencies(d.name));\n\n this.dots = this.canvas\n .selectAll(\"g.dot\")\n .data(data)\n .enter()\n .append(\"g\")\n .attr(\"class\", \"dot\")\n .style(\"fill\", d => scales.currencies(d.name))\n .selectAll(\"circle\")\n .data(d => d.values)\n .enter()\n .append(\"circle\")\n .attr(\"r\", 3);\n\n const canvasNode = this.canvas.node()!;\n this.canvas\n .on(\"mousemove\", () => {\n const matrix = canvasNode.getScreenCTM();\n const d = this.quadtree.find(...clientPoint(canvasNode, event));\n if (this.tooltipText && matrix && d) {\n tooltip\n .style(\"opacity\", 1)\n .html(this.tooltipText(d))\n .style(\"left\", `${window.scrollX + this.x(d.date) + matrix.e}px`)\n .style(\n \"top\",\n `${window.scrollY + this.y(d.value) + matrix.f - 15}px`\n );\n } else {\n tooltip.style(\"opacity\", 0);\n }\n })\n .on(\"mouseleave\", () => {\n tooltip.style(\"opacity\", 0);\n });\n\n this.update();\n return this;\n }\n\n update() {\n this.setHeight(250);\n\n this.y.range([this.height, 0]);\n this.x.range([0, this.width]);\n\n this.canvas.attr(\n \"transform\",\n `translate(${this.margin.left},${this.margin.top})`\n );\n\n this.yAxis.tickSize(-this.width);\n this.xAxisSelection.attr(\"transform\", `translate(0,${this.height})`);\n\n this.xAxisSelection.call(this.xAxis);\n this.yAxisSelection.call(this.yAxis);\n this.dots.attr(\"cx\", d => this.x(d.date)).attr(\"cy\", d => this.y(d.value));\n this.lines.attr(\"d\", d => this.line(d.values));\n\n this.quadtree = quadtree(\n merge(this.data.map(d => d.values)),\n d => this.x(d.date),\n d => this.y(d.value)\n );\n\n this.legend = {\n domain: this.data.map(d => d.name),\n scale: scales.currencies,\n };\n }\n}\n","import { extent } from \"d3-array\";\nimport { axisLeft, axisBottom, Axis } from \"d3-axis\";\nimport { scalePoint, scaleUtc, ScaleTime, ScalePoint } from \"d3-scale\";\nimport { event, clientPoint, select, Selection } from \"d3-selection\";\nimport { quadtree, Quadtree } from \"d3-quadtree\";\nimport \"d3-transition\";\n\nimport { BaseChart } from \"./base\";\nimport { scales } from \"./helpers\";\nimport { dateFormat } from \"../format\";\nimport { tooltip } from \"./tooltip\";\n\nexport interface ScatterPlotDatum {\n date: Date;\n type: string;\n description: string;\n}\n\nexport class ScatterPlot extends BaseChart {\n canvas: Selection;\n\n data: ScatterPlotDatum[];\n\n x: ScaleTime;\n\n y: ScalePoint;\n\n xAxis: Axis;\n\n xAxisSelection: Selection;\n\n yAxis: Axis;\n\n yAxisSelection: Selection;\n\n dots: Selection;\n\n quadtree: Quadtree;\n\n constructor(svg: SVGElement) {\n super(svg);\n this.canvas = select(this.svg)\n .classed(\"scatterplot\", true)\n .append(\"g\");\n this.margin.left = 70;\n\n this.x = scaleUtc();\n this.y = scalePoint().padding(1);\n\n this.xAxis = axisBottom(this.x).tickSizeOuter(0);\n\n this.yAxis = axisLeft(this.y)\n .tickPadding(6)\n .tickFormat(d => d);\n this.data = [];\n this.quadtree = quadtree();\n\n this.xAxisSelection = this.canvas.append(\"g\").attr(\"class\", \"x axis\");\n this.yAxisSelection = this.canvas.append(\"g\").attr(\"class\", \"y axis\");\n this.dots = this.canvas.selectAll(\".dot\");\n }\n\n draw(data: ScatterPlotDatum[]) {\n this.data = data;\n const dateExtent = extent(data, d => d.date);\n if (dateExtent[0] !== undefined) {\n this.x.domain(dateExtent);\n }\n this.y.domain(data.map(d => d.type));\n\n this.dots = this.canvas\n .selectAll(\".dot\")\n .data(this.data)\n .enter()\n .append(\"circle\")\n .attr(\"class\", \"dot\")\n .attr(\"r\", 5)\n .style(\"fill\", d => scales.scatterplot(d.type));\n\n const canvasNode = this.canvas.node()!;\n this.canvas\n .on(\"mousemove\", () => {\n const matrix = canvasNode.getScreenCTM();\n if (!matrix) {\n return;\n }\n const d = this.quadtree.find(...clientPoint(canvasNode, event));\n if (d) {\n tooltip\n .style(\"opacity\", 1)\n .html(this.tooltipText(d))\n .style(\"left\", `${window.scrollX + this.x(d.date) + matrix.e}px`)\n .style(\n \"top\",\n `${window.scrollY + this.y(d.type)! + matrix.f - 15}px`\n );\n } else {\n tooltip.style(\"opacity\", 0);\n }\n })\n .on(\"mouseleave\", () => {\n tooltip.style(\"opacity\", 0);\n });\n\n this.update();\n return this;\n }\n\n // eslint-disable-next-line class-methods-use-this\n tooltipText(d: ScatterPlotDatum) {\n return `${d.description}${dateFormat.day(d.date)}`;\n }\n\n update() {\n this.setHeight(250);\n\n this.y.range([this.height, 0]);\n this.x.range([0, this.width]);\n\n this.canvas.attr(\n \"transform\",\n `translate(${this.margin.left},${this.margin.top})`\n );\n\n this.yAxis.tickSize(-this.width);\n this.xAxisSelection.attr(\"transform\", `translate(0,${this.height})`);\n\n this.xAxisSelection.call(this.xAxis);\n this.yAxisSelection.call(this.yAxis);\n this.dots.attr(\"cx\", d => this.x(d.date)).attr(\"cy\", d => this.y(d.type)!);\n\n this.quadtree = quadtree(\n this.data,\n d => this.x(d.date),\n d => this.y(d.type)!\n );\n }\n}\n","import {\n partition,\n PartitionLayout,\n treemap,\n TreemapLayout,\n HierarchyNode,\n HierarchyRectangularNode,\n} from \"d3-hierarchy\";\nimport { scaleLinear, scaleSqrt, ScaleLinear, ScalePower } from \"d3-scale\";\nimport { event, select, Selection } from \"d3-selection\";\nimport { arc, Arc } from \"d3-shape\";\nimport \"d3-transition\";\n\nimport { favaAPI } from \"../stores\";\nimport { formatCurrency, formatPercentage } from \"../format\";\n\nimport { BaseChart } from \"./base\";\nimport { NO_MARGINS, scales } from \"./helpers\";\nimport { addTooltip } from \"./tooltip\";\n\ninterface AccountHierarchyDatum {\n account: string;\n balance: Record;\n dummy?: boolean;\n}\nexport interface AccountHierarchy extends AccountHierarchyDatum {\n children: AccountHierarchy[];\n}\nexport type AccountHierarchyNode = HierarchyNode;\n\n/**\n * Add internal nodes as fake leaf nodes to their own children.\n *\n * In the treemap, we only render leaf nodes, so for accounts that have both\n * children and a balance, we want to duplicate them as leaf nodes.\n */\nexport function addInternalNodesAsLeaves(node: AccountHierarchy) {\n if (node.children.length) {\n node.children.forEach(addInternalNodesAsLeaves);\n node.children.push({ ...node, children: [], dummy: true });\n node.balance = {};\n }\n}\n\n// Turn the elements in the selection (assuming they have a .account attribute)\n// into links to the account page.\nfunction makeAccountLink(\n selection:\n | Selection\n | Selection\n | Selection\n) {\n selection.on(\"click\", d => {\n window.location.href = favaAPI.accountURL.replace(\n \"REPLACEME\",\n d.data.account\n );\n event.stopPropagation();\n });\n}\n\ntype TreemapDatum = HierarchyRectangularNode;\n\nclass TreeMapChart extends BaseChart {\n treemap: TreemapLayout;\n\n root?: TreemapDatum;\n\n canvas: Selection;\n\n cells: Selection;\n\n labels: Selection;\n\n tooltipText?: (d: TreemapDatum) => string;\n\n constructor(svg: SVGElement) {\n super(svg);\n this.treemap = treemap().paddingInner(2);\n this.margin = NO_MARGINS;\n\n this.canvas = select(svg).classed(\"treemap\", true);\n this.cells = this.canvas.selectAll(\"g\");\n this.labels = this.cells.append(\"text\");\n }\n\n draw(data: AccountHierarchyNode) {\n this.root = this.treemap(data);\n\n this.cells = this.canvas\n .selectAll(\"g\")\n .data(this.root.leaves())\n .enter()\n .append(\"g\")\n .call(addTooltip, this.tooltipText);\n\n this.cells.append(\"rect\").attr(\"fill\", d => {\n const node = d.data.dummy ? d.parent! : d;\n if (node.parent === this.root || !node.parent) {\n return scales.treemap(node.data.account);\n }\n return scales.treemap(node.parent.data.account);\n });\n\n this.labels = this.cells\n .append(\"text\")\n .attr(\"dy\", \".5em\")\n .attr(\"text-anchor\", \"middle\")\n .text(d => d.data.account.split(\":\").pop() || \"\")\n .style(\"opacity\", 0)\n .call(makeAccountLink);\n\n this.update();\n return this;\n }\n\n update() {\n this.setHeight(Math.min(this.width / 2.5, 400));\n\n if (!this.root) {\n return;\n }\n\n this.treemap.size([this.width, this.height]);\n this.treemap(this.root);\n\n function labelOpacity(this: SVGTextElement, d: TreemapDatum) {\n const length = this.getComputedTextLength();\n return d.x1 - d.x0 > length + 4 && d.y1 - d.y0 > 14 ? 1 : 0;\n }\n\n this.cells.attr(\"transform\", d => `translate(${d.x0},${d.y0})`);\n\n this.cells\n .select(\"rect\")\n .attr(\"width\", d => d.x1 - d.x0)\n .attr(\"height\", d => d.y1 - d.y0);\n\n this.labels\n .attr(\"x\", d => (d.x1 - d.x0) / 2)\n .attr(\"y\", d => (d.y1 - d.y0) / 2)\n .style(\"opacity\", labelOpacity);\n }\n}\n\nclass SunburstChart extends BaseChart {\n canvas: Selection;\n\n root?: TreemapDatum;\n\n arc: Arc;\n\n x: ScaleLinear;\n\n y: ScalePower;\n\n partition: PartitionLayout;\n\n labelText?: (d: TreemapDatum) => string;\n\n accountLabel: Selection;\n\n balanceLabel: Selection;\n\n paths: Selection;\n\n boundingCircle: Selection;\n\n constructor(svg: SVGElement) {\n super(svg);\n this.margin = NO_MARGINS;\n\n this.x = scaleLinear().range([0, 2 * Math.PI]);\n this.y = scaleSqrt();\n this.partition = partition();\n this.arc = arc()\n .startAngle(d => this.x(d.x0))\n .endAngle(d => this.x(d.x1))\n .innerRadius(d => this.y(d.y0))\n .outerRadius(d => this.y(d.y1));\n\n this.canvas = select(this.svg)\n .attr(\"class\", \"sunburst\")\n .append(\"g\")\n .on(\"mouseleave\", () => this.mouseLeave());\n\n // Bounding circle underneath the sunburst\n this.boundingCircle = this.canvas.append(\"circle\").style(\"opacity\", 0);\n\n this.accountLabel = this.canvas\n .append(\"text\")\n .attr(\"class\", \"account\")\n .attr(\"text-anchor\", \"middle\");\n\n this.balanceLabel = this.canvas\n .append(\"text\")\n .attr(\"class\", \"balance\")\n .attr(\"dy\", \"1.2em\")\n .attr(\"text-anchor\", \"middle\");\n\n this.paths = this.canvas.selectAll(\"path\");\n }\n\n draw(data: AccountHierarchyNode) {\n this.root = this.partition(data);\n\n this.paths = this.canvas\n .selectAll(\"path\")\n .data(this.root.descendants())\n .enter()\n .filter(d => !d.data.dummy && !!d.depth)\n .append(\"path\")\n .attr(\"fill-rule\", \"evenodd\")\n .style(\"fill\", d => scales.sunburst(d.data.account))\n .on(\"mouseover\", d => this.mouseOver(d))\n .call(makeAccountLink);\n\n this.update();\n this.setLabel(this.root);\n return this;\n }\n\n update() {\n this.canvas.attr(\n \"transform\",\n `translate(${this.width / 2 + this.margin.left},${this.height / 2 +\n this.margin.top})`\n );\n\n const radius = Math.min(this.width, this.height) / 2;\n this.boundingCircle.attr(\"r\", radius);\n this.y.range([0, radius]);\n this.paths.attr(\"d\", this.arc);\n }\n\n setLabel(d: TreemapDatum) {\n if (this.labelText) {\n this.balanceLabel.text(this.labelText(d));\n }\n this.accountLabel\n .datum(d)\n .text(d.data.account)\n .call(makeAccountLink);\n }\n\n // Fade all but the current sequence\n mouseOver(d: TreemapDatum) {\n this.setLabel(d);\n this.paths.interrupt();\n\n // Only highlight segments that are ancestors of the current segment.\n this.paths\n .style(\"opacity\", 0.5)\n // check if d.account starts with node.account\n .filter(node => d.data.account.lastIndexOf(node.data.account, 0) === 0)\n .style(\"opacity\", 1);\n }\n\n // Restore everything to full opacity when moving off the visualization.\n mouseLeave() {\n this.paths\n .transition()\n .duration(1000)\n .style(\"opacity\", 1);\n if (this.root) {\n this.setLabel(this.root);\n }\n }\n}\n\nclass SunburstChartContainer extends BaseChart {\n currencies: string[];\n\n canvases: Selection[];\n\n sunbursts: SunburstChart[];\n\n constructor(svg: SVGElement) {\n super(svg);\n\n this.svg.setAttribute(\"class\", \"sunburst\");\n this.sunbursts = [];\n this.canvases = [];\n this.margin = NO_MARGINS;\n this.currencies = [];\n\n this.setHeight(500);\n }\n\n draw(data: Record) {\n this.currencies = Object.keys(data);\n\n this.currencies.forEach((currency, i) => {\n const canvas = select(this.svg)\n .append(\"g\")\n .attr(\n \"transform\",\n `translate(${(this.width * i) / this.currencies.length},0)`\n );\n\n const totalBalance = data[currency].value || 1;\n const sunburst = new SunburstChart(canvas.node()!)\n .setWidth(this.width / this.currencies.length)\n .setHeight(500)\n .set(\"labelText\", d => {\n const balance = d.value || 0;\n return `${formatCurrency(balance)} ${currency} (${formatPercentage(\n balance / totalBalance\n )})`;\n })\n .draw(data[currency]);\n\n this.canvases.push(canvas);\n this.sunbursts.push(sunburst);\n });\n\n return this;\n }\n\n update() {\n this.sunbursts.forEach((singleChart, i) => {\n singleChart\n .setWidth(this.width / this.currencies.length)\n .setHeight(500)\n .update();\n this.canvases[i].attr(\n \"transform\",\n `translate(${(this.width * i) / this.currencies.length},0)`\n );\n });\n }\n}\n\nexport class HierarchyContainer extends BaseChart {\n canvas: SVGGElement;\n\n data?: Record;\n\n currency: string;\n\n currencies: string[];\n\n currentChart?: TreeMapChart | SunburstChartContainer;\n\n mode: \"treemap\" | \"sunburst\";\n\n constructor(svg: SVGElement) {\n super(svg);\n this.canvas = document.createElementNS(\"http://www.w3.org/2000/svg\", \"g\");\n svg.appendChild(this.canvas);\n this.has_mode_setting = true;\n this.margin = NO_MARGINS;\n this.currencies = [];\n this.currency = \"\";\n this.mode = \"treemap\";\n }\n\n draw(data: Record) {\n this.data = data;\n this.currencies = Object.keys(data);\n\n this.canvas.innerHTML = \"\";\n\n if (this.currencies.length === 0) {\n select(this.canvas)\n .append(\"text\")\n .text(\"Chart is empty.\")\n .attr(\"text-anchor\", \"middle\")\n .attr(\"x\", this.width / 2)\n .attr(\"y\", 160 / 2);\n } else if (this.mode === \"treemap\") {\n if (!this.currency) {\n [this.currency] = this.currencies;\n }\n const totalBalance = data[this.currency].value || 1;\n const currentChart = new TreeMapChart(this.canvas)\n .setWidth(this.width)\n .set(\"tooltipText\", d => {\n const balance = d.data.balance[this.currency];\n return `${formatCurrency(balance)} ${\n this.currency\n } (${formatPercentage(balance / totalBalance)})${\n d.data.account\n }`;\n })\n .draw(data[this.currency]);\n\n this.setHeight(currentChart.outerHeight);\n this.currentChart = currentChart;\n this.has_currency_setting = true;\n } else {\n this.currentChart = new SunburstChartContainer(this.canvas)\n .setWidth(this.width)\n .draw(data);\n\n this.setHeight(this.currentChart.outerHeight);\n this.has_currency_setting = false;\n }\n\n return this;\n }\n\n update() {\n if (!this.data) {\n return;\n }\n this.draw(this.data);\n if (this.currentChart) {\n this.currentChart.setWidth(this.outerWidth).update();\n }\n }\n}\n","/**\n * This module contains the main code to render Fava's charts.\n *\n * The charts heavily use d3 libraries.\n */\n\nimport { group } from \"d3-array\";\nimport { hierarchy } from \"d3-hierarchy\";\nimport \"d3-transition\";\nimport { get } from \"svelte/store\";\n\nimport { getScriptTagJSON } from \"../helpers\";\nimport { favaAPI, conversion } from \"../stores\";\nimport e from \"../events\";\nimport { formatCurrency, dateFormat, currentDateFormat } from \"../format\";\nimport {\n array,\n date,\n object,\n record,\n number,\n string,\n tuple,\n unknown,\n lazy,\n Validator,\n} from \"../validation\";\n\nimport { BaseChart } from \"./base\";\nimport { BarChart } from \"./bar\";\nimport { LineChart, LineChartDatum } from \"./line\";\nimport { ScatterPlot } from \"./scatter\";\nimport {\n addInternalNodesAsLeaves,\n HierarchyContainer,\n AccountHierarchy,\n AccountHierarchyNode,\n} from \"./hierarchy\";\nimport { scales } from \"./helpers\";\n\n/**\n * The list of operating currencies, adding in the current conversion currency.\n */\nlet operatingCurrenciesWithConversion: string[] = [];\nconversion.subscribe(conversionValue => {\n if (\n !conversionValue ||\n [\"at_cost\", \"at_value\", \"units\"].includes(conversionValue) ||\n favaAPI.options.operating_currency.includes(conversionValue)\n ) {\n operatingCurrenciesWithConversion = favaAPI.options.operating_currency;\n } else {\n operatingCurrenciesWithConversion = [\n ...favaAPI.options.operating_currency,\n conversionValue,\n ];\n }\n});\n\ne.on(\"page-init\", () => {\n const { accounts, options } = favaAPI;\n scales.treemap.domain(accounts);\n scales.sunburst.domain(accounts);\n options.operating_currency.sort();\n options.commodities.sort();\n scales.currencies.domain([\n ...options.operating_currency,\n ...options.commodities,\n ]);\n});\n\ninterface ChartWithData {\n data: Parameters[0];\n renderer: (svg: SVGElement) => T;\n}\n\nconst parsers: Record<\n string,\n (json: unknown, label: string) => ChartWithData\n> = {\n balances(json: unknown): ChartWithData {\n const parsedData = array(\n object({\n date,\n balance: record(number),\n })\n )(json);\n const allValues: LineChartDatum[] = [];\n for (const { date: date_, balance } of parsedData) {\n Object.entries(balance).forEach(([currency, value]) => {\n allValues.push({\n name: currency,\n date: date_,\n value,\n });\n });\n }\n const data = [...group(allValues, v => v.name).entries()].map(\n ([name, values]) => ({\n name,\n values,\n })\n );\n\n return {\n data,\n renderer: (svg: SVGElement) =>\n new LineChart(svg).set(\n \"tooltipText\",\n d =>\n `${formatCurrency(d.value)} ${d.name}${dateFormat.day(\n d.date\n )}`\n ),\n };\n },\n commodities(json: unknown, label: string): ChartWithData {\n const parsedData = object({\n quote: string,\n base: string,\n prices: array(tuple([date, number])),\n })(json);\n\n const renderer = (svg: SVGElement) =>\n new LineChart(svg).set(\n \"tooltipText\",\n d =>\n `1 ${parsedData.base} = ${formatCurrency(d.value)} ${\n parsedData.quote\n }${dateFormat.day(d.date)}`\n );\n return {\n data: [\n {\n name: label,\n values: parsedData.prices.map(d => ({\n name: label,\n date: d[0],\n value: d[1],\n })),\n },\n ],\n renderer,\n };\n },\n bar(json: unknown): ChartWithData {\n const jsonData = array(\n object({ date, budgets: record(number), balance: record(number) })\n )(json);\n const currentDateFmt = get(currentDateFormat);\n const data = jsonData.map(d => ({\n values: operatingCurrenciesWithConversion.map(name => ({\n name,\n value: d.balance[name] || 0,\n budget: d.budgets[name] || 0,\n })),\n date: d.date,\n label: currentDateFmt(d.date),\n }));\n const renderer = (svg: SVGElement) =>\n new BarChart(svg).set(\"tooltipText\", d => {\n let text = \"\";\n d.values.forEach(a => {\n text += `${formatCurrency(a.value)} ${a.name}`;\n if (a.budget) {\n text += ` / ${formatCurrency(a.budget)} ${a.name}`;\n }\n text += \"
\";\n });\n text += `${d.label}`;\n return text;\n });\n return { data, renderer };\n },\n hierarchy(json: unknown): ChartWithData {\n const hierarchyValidator: Validator = object({\n account: string,\n balance: record(number),\n balance_children: record(number),\n children: lazy(() => array(hierarchyValidator)),\n });\n const validator = object({\n root: hierarchyValidator,\n modifier: number,\n });\n const { root, modifier } = validator(json);\n addInternalNodesAsLeaves(root);\n const data: Record = {};\n\n operatingCurrenciesWithConversion.forEach(currency => {\n const currencyHierarchy: AccountHierarchyNode = hierarchy(root)\n .sum(d => d.balance[currency] * modifier)\n .sort((a, b) => (b.value || 0) - (a.value || 0));\n if (currencyHierarchy.value) {\n data[currency] = currencyHierarchy;\n }\n });\n\n return {\n data,\n renderer: (svg: SVGElement) => new HierarchyContainer(svg),\n };\n },\n scatterplot(json: unknown): ChartWithData {\n const parser = array(\n object({\n type: string,\n date,\n description: string,\n })\n );\n return {\n data: parser(json),\n renderer: (svg: SVGElement) => new ScatterPlot(svg),\n };\n },\n};\n\nexport function parseChartData() {\n const chartData = array(\n object({\n label: string,\n type: string,\n data: unknown,\n })\n )(getScriptTagJSON(\"#chart-data\"));\n const result: (ChartWithData & { name: string })[] = [];\n chartData.forEach(chart => {\n const parser = parsers[chart.type];\n if (parser) {\n result.push({\n name: chart.label,\n ...parser(chart.data, chart.label),\n });\n }\n });\n return result;\n}\n\nexport function parseQueryChart(\n data: unknown\n): ChartWithData | undefined {\n if (!Array.isArray(data) || !data.length) {\n return undefined;\n }\n if (data[0].group !== undefined) {\n const validated = array(object({ group: string, balance: record(number) }))(\n data\n );\n const root: AccountHierarchy = {\n account: \"(root)\",\n balance: {},\n children: [],\n };\n const accountMap: Map = new Map([\n [root.account, root],\n ]);\n const addNode = (node: AccountHierarchy) => {\n const name = node.account;\n const existing = accountMap.get(name);\n if (existing) {\n existing.balance = node.balance;\n return;\n }\n accountMap.set(name, node);\n const parentEnd = name.lastIndexOf(\":\");\n const parentId = parentEnd > 0 ? name.slice(0, parentEnd) : root.account;\n let parent = accountMap.get(parentId);\n if (!parent) {\n parent = { account: parentId, balance: {}, children: [] };\n addNode(parent);\n }\n parent.children.push(node);\n };\n for (const { group: account = \"(empty)\", balance } of validated) {\n addNode({ account, balance, children: [] });\n }\n\n const chartData: Record = {};\n operatingCurrenciesWithConversion.forEach(currency => {\n const currencyHierarchy: AccountHierarchyNode = hierarchy(root)\n .sum(d => d.balance[currency])\n .sort((a, b) => (b.value || 0) - (a.value || 0));\n if (currencyHierarchy.value !== undefined) {\n chartData[currency] = currencyHierarchy;\n }\n });\n\n return {\n data: chartData,\n renderer: (svg: SVGElement) => new HierarchyContainer(svg),\n };\n }\n if (data[0].date !== undefined) {\n return parsers.balances(data, \"\");\n }\n return undefined;\n}\n","import e from \"./events\";\nimport { select, selectAll } from \"./helpers\";\n\n// Copy the given text to the clipboard.\nfunction copyToClipboard(text: string | null) {\n if (!text) {\n return;\n }\n const textarea = document.createElement(\"textarea\");\n textarea.value = text;\n textarea.style.position = \"fixed\";\n textarea.style.top = \"0\";\n textarea.style.left = \"0\";\n document.body.appendChild(textarea);\n textarea.focus();\n textarea.select();\n\n try {\n document.execCommand(\"copy\");\n } catch (err) {\n console.error(\"Unable to copy\", err); // eslint-disable-line no-console\n }\n textarea.remove();\n}\n\ne.on(\"page-loaded\", () => {\n selectAll(\".status-indicator\").forEach(indicator => {\n indicator.addEventListener(\"click\", () => {\n copyToClipboard(indicator.getAttribute(\"data-clipboard-text\"));\n });\n });\n\n const copyBalances = select(\"#copy-balances\");\n if (copyBalances) {\n copyBalances.addEventListener(\"click\", () => {\n copyToClipboard(copyBalances.getAttribute(\"data-clipboard-text\"));\n });\n }\n});\n","// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/LICENSE\n\n// This is CodeMirror (https://codemirror.net), a code editor\n// implemented in JavaScript on top of the browser's DOM.\n//\n// You can find some technical background for some of the code below\n// at http://marijnhaverbeke.nl/blog/#cm-internals .\n\n(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (global = global || self, global.CodeMirror = factory());\n}(this, (function () { 'use strict';\n\n // Kludges for bugs and behavior differences that can't be feature\n // detected are enabled based on userAgent etc sniffing.\n var userAgent = navigator.userAgent;\n var platform = navigator.platform;\n\n var gecko = /gecko\\/\\d/i.test(userAgent);\n var ie_upto10 = /MSIE \\d/.test(userAgent);\n var ie_11up = /Trident\\/(?:[7-9]|\\d{2,})\\..*rv:(\\d+)/.exec(userAgent);\n var edge = /Edge\\/(\\d+)/.exec(userAgent);\n var ie = ie_upto10 || ie_11up || edge;\n var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]);\n var webkit = !edge && /WebKit\\//.test(userAgent);\n var qtwebkit = webkit && /Qt\\/\\d+\\.\\d+/.test(userAgent);\n var chrome = !edge && /Chrome\\//.test(userAgent);\n var presto = /Opera\\//.test(userAgent);\n var safari = /Apple Computer/.test(navigator.vendor);\n var mac_geMountainLion = /Mac OS X 1\\d\\D([8-9]|\\d\\d)\\D/.test(userAgent);\n var phantom = /PhantomJS/.test(userAgent);\n\n var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\\/\\w+/.test(userAgent);\n var android = /Android/.test(userAgent);\n // This is woefully incomplete. Suggestions for alternative methods welcome.\n var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);\n var mac = ios || /Mac/.test(platform);\n var chromeOS = /\\bCrOS\\b/.test(userAgent);\n var windows = /win/i.test(platform);\n\n var presto_version = presto && userAgent.match(/Version\\/(\\d*\\.\\d*)/);\n if (presto_version) { presto_version = Number(presto_version[1]); }\n if (presto_version && presto_version >= 15) { presto = false; webkit = true; }\n // Some browsers use the wrong event properties to signal cmd/ctrl on OS X\n var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));\n var captureRightClick = gecko || (ie && ie_version >= 9);\n\n function classTest(cls) { return new RegExp(\"(^|\\\\s)\" + cls + \"(?:$|\\\\s)\\\\s*\") }\n\n var rmClass = function(node, cls) {\n var current = node.className;\n var match = classTest(cls).exec(current);\n if (match) {\n var after = current.slice(match.index + match[0].length);\n node.className = current.slice(0, match.index) + (after ? match[1] + after : \"\");\n }\n };\n\n function removeChildren(e) {\n for (var count = e.childNodes.length; count > 0; --count)\n { e.removeChild(e.firstChild); }\n return e\n }\n\n function removeChildrenAndAdd(parent, e) {\n return removeChildren(parent).appendChild(e)\n }\n\n function elt(tag, content, className, style) {\n var e = document.createElement(tag);\n if (className) { e.className = className; }\n if (style) { e.style.cssText = style; }\n if (typeof content == \"string\") { e.appendChild(document.createTextNode(content)); }\n else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } }\n return e\n }\n // wrapper for elt, which removes the elt from the accessibility tree\n function eltP(tag, content, className, style) {\n var e = elt(tag, content, className, style);\n e.setAttribute(\"role\", \"presentation\");\n return e\n }\n\n var range;\n if (document.createRange) { range = function(node, start, end, endNode) {\n var r = document.createRange();\n r.setEnd(endNode || node, end);\n r.setStart(node, start);\n return r\n }; }\n else { range = function(node, start, end) {\n var r = document.body.createTextRange();\n try { r.moveToElementText(node.parentNode); }\n catch(e) { return r }\n r.collapse(true);\n r.moveEnd(\"character\", end);\n r.moveStart(\"character\", start);\n return r\n }; }\n\n function contains(parent, child) {\n if (child.nodeType == 3) // Android browser always returns false when child is a textnode\n { child = child.parentNode; }\n if (parent.contains)\n { return parent.contains(child) }\n do {\n if (child.nodeType == 11) { child = child.host; }\n if (child == parent) { return true }\n } while (child = child.parentNode)\n }\n\n function activeElt() {\n // IE and Edge may throw an \"Unspecified Error\" when accessing document.activeElement.\n // IE < 10 will throw when accessed while the page is loading or in an iframe.\n // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.\n var activeElement;\n try {\n activeElement = document.activeElement;\n } catch(e) {\n activeElement = document.body || null;\n }\n while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)\n { activeElement = activeElement.shadowRoot.activeElement; }\n return activeElement\n }\n\n function addClass(node, cls) {\n var current = node.className;\n if (!classTest(cls).test(current)) { node.className += (current ? \" \" : \"\") + cls; }\n }\n function joinClasses(a, b) {\n var as = a.split(\" \");\n for (var i = 0; i < as.length; i++)\n { if (as[i] && !classTest(as[i]).test(b)) { b += \" \" + as[i]; } }\n return b\n }\n\n var selectInput = function(node) { node.select(); };\n if (ios) // Mobile Safari apparently has a bug where select() is broken.\n { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; }\n else if (ie) // Suppress mysterious IE10 errors\n { selectInput = function(node) { try { node.select(); } catch(_e) {} }; }\n\n function bind(f) {\n var args = Array.prototype.slice.call(arguments, 1);\n return function(){return f.apply(null, args)}\n }\n\n function copyObj(obj, target, overwrite) {\n if (!target) { target = {}; }\n for (var prop in obj)\n { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))\n { target[prop] = obj[prop]; } }\n return target\n }\n\n // Counts the column offset in a string, taking tabs into account.\n // Used mostly to find indentation.\n function countColumn(string, end, tabSize, startIndex, startValue) {\n if (end == null) {\n end = string.search(/[^\\s\\u00a0]/);\n if (end == -1) { end = string.length; }\n }\n for (var i = startIndex || 0, n = startValue || 0;;) {\n var nextTab = string.indexOf(\"\\t\", i);\n if (nextTab < 0 || nextTab >= end)\n { return n + (end - i) }\n n += nextTab - i;\n n += tabSize - (n % tabSize);\n i = nextTab + 1;\n }\n }\n\n var Delayed = function() {\n this.id = null;\n this.f = null;\n this.time = 0;\n this.handler = bind(this.onTimeout, this);\n };\n Delayed.prototype.onTimeout = function (self) {\n self.id = 0;\n if (self.time <= +new Date) {\n self.f();\n } else {\n setTimeout(self.handler, self.time - +new Date);\n }\n };\n Delayed.prototype.set = function (ms, f) {\n this.f = f;\n var time = +new Date + ms;\n if (!this.id || time < this.time) {\n clearTimeout(this.id);\n this.id = setTimeout(this.handler, ms);\n this.time = time;\n }\n };\n\n function indexOf(array, elt) {\n for (var i = 0; i < array.length; ++i)\n { if (array[i] == elt) { return i } }\n return -1\n }\n\n // Number of pixels added to scroller and sizer to hide scrollbar\n var scrollerGap = 30;\n\n // Returned or thrown by various protocols to signal 'I'm not\n // handling this'.\n var Pass = {toString: function(){return \"CodeMirror.Pass\"}};\n\n // Reused option objects for setSelection & friends\n var sel_dontScroll = {scroll: false}, sel_mouse = {origin: \"*mouse\"}, sel_move = {origin: \"+move\"};\n\n // The inverse of countColumn -- find the offset that corresponds to\n // a particular column.\n function findColumn(string, goal, tabSize) {\n for (var pos = 0, col = 0;;) {\n var nextTab = string.indexOf(\"\\t\", pos);\n if (nextTab == -1) { nextTab = string.length; }\n var skipped = nextTab - pos;\n if (nextTab == string.length || col + skipped >= goal)\n { return pos + Math.min(skipped, goal - col) }\n col += nextTab - pos;\n col += tabSize - (col % tabSize);\n pos = nextTab + 1;\n if (col >= goal) { return pos }\n }\n }\n\n var spaceStrs = [\"\"];\n function spaceStr(n) {\n while (spaceStrs.length <= n)\n { spaceStrs.push(lst(spaceStrs) + \" \"); }\n return spaceStrs[n]\n }\n\n function lst(arr) { return arr[arr.length-1] }\n\n function map(array, f) {\n var out = [];\n for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); }\n return out\n }\n\n function insertSorted(array, value, score) {\n var pos = 0, priority = score(value);\n while (pos < array.length && score(array[pos]) <= priority) { pos++; }\n array.splice(pos, 0, value);\n }\n\n function nothing() {}\n\n function createObj(base, props) {\n var inst;\n if (Object.create) {\n inst = Object.create(base);\n } else {\n nothing.prototype = base;\n inst = new nothing();\n }\n if (props) { copyObj(props, inst); }\n return inst\n }\n\n var nonASCIISingleCaseWordChar = /[\\u00df\\u0587\\u0590-\\u05f4\\u0600-\\u06ff\\u3040-\\u309f\\u30a0-\\u30ff\\u3400-\\u4db5\\u4e00-\\u9fcc\\uac00-\\ud7af]/;\n function isWordCharBasic(ch) {\n return /\\w/.test(ch) || ch > \"\\x80\" &&\n (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))\n }\n function isWordChar(ch, helper) {\n if (!helper) { return isWordCharBasic(ch) }\n if (helper.source.indexOf(\"\\\\w\") > -1 && isWordCharBasic(ch)) { return true }\n return helper.test(ch)\n }\n\n function isEmpty(obj) {\n for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }\n return true\n }\n\n // Extending unicode characters. A series of a non-extending char +\n // any number of extending chars is treated as a single unit as far\n // as editing and measuring is concerned. This is not fully correct,\n // since some scripts/fonts/browsers also treat other configurations\n // of code points as a group.\n var extendingChars = /[\\u0300-\\u036f\\u0483-\\u0489\\u0591-\\u05bd\\u05bf\\u05c1\\u05c2\\u05c4\\u05c5\\u05c7\\u0610-\\u061a\\u064b-\\u065e\\u0670\\u06d6-\\u06dc\\u06de-\\u06e4\\u06e7\\u06e8\\u06ea-\\u06ed\\u0711\\u0730-\\u074a\\u07a6-\\u07b0\\u07eb-\\u07f3\\u0816-\\u0819\\u081b-\\u0823\\u0825-\\u0827\\u0829-\\u082d\\u0900-\\u0902\\u093c\\u0941-\\u0948\\u094d\\u0951-\\u0955\\u0962\\u0963\\u0981\\u09bc\\u09be\\u09c1-\\u09c4\\u09cd\\u09d7\\u09e2\\u09e3\\u0a01\\u0a02\\u0a3c\\u0a41\\u0a42\\u0a47\\u0a48\\u0a4b-\\u0a4d\\u0a51\\u0a70\\u0a71\\u0a75\\u0a81\\u0a82\\u0abc\\u0ac1-\\u0ac5\\u0ac7\\u0ac8\\u0acd\\u0ae2\\u0ae3\\u0b01\\u0b3c\\u0b3e\\u0b3f\\u0b41-\\u0b44\\u0b4d\\u0b56\\u0b57\\u0b62\\u0b63\\u0b82\\u0bbe\\u0bc0\\u0bcd\\u0bd7\\u0c3e-\\u0c40\\u0c46-\\u0c48\\u0c4a-\\u0c4d\\u0c55\\u0c56\\u0c62\\u0c63\\u0cbc\\u0cbf\\u0cc2\\u0cc6\\u0ccc\\u0ccd\\u0cd5\\u0cd6\\u0ce2\\u0ce3\\u0d3e\\u0d41-\\u0d44\\u0d4d\\u0d57\\u0d62\\u0d63\\u0dca\\u0dcf\\u0dd2-\\u0dd4\\u0dd6\\u0ddf\\u0e31\\u0e34-\\u0e3a\\u0e47-\\u0e4e\\u0eb1\\u0eb4-\\u0eb9\\u0ebb\\u0ebc\\u0ec8-\\u0ecd\\u0f18\\u0f19\\u0f35\\u0f37\\u0f39\\u0f71-\\u0f7e\\u0f80-\\u0f84\\u0f86\\u0f87\\u0f90-\\u0f97\\u0f99-\\u0fbc\\u0fc6\\u102d-\\u1030\\u1032-\\u1037\\u1039\\u103a\\u103d\\u103e\\u1058\\u1059\\u105e-\\u1060\\u1071-\\u1074\\u1082\\u1085\\u1086\\u108d\\u109d\\u135f\\u1712-\\u1714\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17b7-\\u17bd\\u17c6\\u17c9-\\u17d3\\u17dd\\u180b-\\u180d\\u18a9\\u1920-\\u1922\\u1927\\u1928\\u1932\\u1939-\\u193b\\u1a17\\u1a18\\u1a56\\u1a58-\\u1a5e\\u1a60\\u1a62\\u1a65-\\u1a6c\\u1a73-\\u1a7c\\u1a7f\\u1b00-\\u1b03\\u1b34\\u1b36-\\u1b3a\\u1b3c\\u1b42\\u1b6b-\\u1b73\\u1b80\\u1b81\\u1ba2-\\u1ba5\\u1ba8\\u1ba9\\u1c2c-\\u1c33\\u1c36\\u1c37\\u1cd0-\\u1cd2\\u1cd4-\\u1ce0\\u1ce2-\\u1ce8\\u1ced\\u1dc0-\\u1de6\\u1dfd-\\u1dff\\u200c\\u200d\\u20d0-\\u20f0\\u2cef-\\u2cf1\\u2de0-\\u2dff\\u302a-\\u302f\\u3099\\u309a\\ua66f-\\ua672\\ua67c\\ua67d\\ua6f0\\ua6f1\\ua802\\ua806\\ua80b\\ua825\\ua826\\ua8c4\\ua8e0-\\ua8f1\\ua926-\\ua92d\\ua947-\\ua951\\ua980-\\ua982\\ua9b3\\ua9b6-\\ua9b9\\ua9bc\\uaa29-\\uaa2e\\uaa31\\uaa32\\uaa35\\uaa36\\uaa43\\uaa4c\\uaab0\\uaab2-\\uaab4\\uaab7\\uaab8\\uaabe\\uaabf\\uaac1\\uabe5\\uabe8\\uabed\\udc00-\\udfff\\ufb1e\\ufe00-\\ufe0f\\ufe20-\\ufe26\\uff9e\\uff9f]/;\n function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }\n\n // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.\n function skipExtendingChars(str, pos, dir) {\n while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; }\n return pos\n }\n\n // Returns the value from the range [`from`; `to`] that satisfies\n // `pred` and is closest to `from`. Assumes that at least `to`\n // satisfies `pred`. Supports `from` being greater than `to`.\n function findFirst(pred, from, to) {\n // At any point we are certain `to` satisfies `pred`, don't know\n // whether `from` does.\n var dir = from > to ? -1 : 1;\n for (;;) {\n if (from == to) { return from }\n var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF);\n if (mid == from) { return pred(mid) ? from : to }\n if (pred(mid)) { to = mid; }\n else { from = mid + dir; }\n }\n }\n\n // BIDI HELPERS\n\n function iterateBidiSections(order, from, to, f) {\n if (!order) { return f(from, to, \"ltr\", 0) }\n var found = false;\n for (var i = 0; i < order.length; ++i) {\n var part = order[i];\n if (part.from < to && part.to > from || from == to && part.to == from) {\n f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? \"rtl\" : \"ltr\", i);\n found = true;\n }\n }\n if (!found) { f(from, to, \"ltr\"); }\n }\n\n var bidiOther = null;\n function getBidiPartAt(order, ch, sticky) {\n var found;\n bidiOther = null;\n for (var i = 0; i < order.length; ++i) {\n var cur = order[i];\n if (cur.from < ch && cur.to > ch) { return i }\n if (cur.to == ch) {\n if (cur.from != cur.to && sticky == \"before\") { found = i; }\n else { bidiOther = i; }\n }\n if (cur.from == ch) {\n if (cur.from != cur.to && sticky != \"before\") { found = i; }\n else { bidiOther = i; }\n }\n }\n return found != null ? found : bidiOther\n }\n\n // Bidirectional ordering algorithm\n // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm\n // that this (partially) implements.\n\n // One-char codes used for character types:\n // L (L): Left-to-Right\n // R (R): Right-to-Left\n // r (AL): Right-to-Left Arabic\n // 1 (EN): European Number\n // + (ES): European Number Separator\n // % (ET): European Number Terminator\n // n (AN): Arabic Number\n // , (CS): Common Number Separator\n // m (NSM): Non-Spacing Mark\n // b (BN): Boundary Neutral\n // s (B): Paragraph Separator\n // t (S): Segment Separator\n // w (WS): Whitespace\n // N (ON): Other Neutrals\n\n // Returns null if characters are ordered as they appear\n // (left-to-right), or an array of sections ({from, to, level}\n // objects) in the order in which they occur visually.\n var bidiOrdering = (function() {\n // Character types for codepoints 0 to 0xff\n var lowTypes = \"bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN\";\n // Character types for codepoints 0x600 to 0x6f9\n var arabicTypes = \"nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111\";\n function charType(code) {\n if (code <= 0xf7) { return lowTypes.charAt(code) }\n else if (0x590 <= code && code <= 0x5f4) { return \"R\" }\n else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }\n else if (0x6ee <= code && code <= 0x8ac) { return \"r\" }\n else if (0x2000 <= code && code <= 0x200b) { return \"w\" }\n else if (code == 0x200c) { return \"b\" }\n else { return \"L\" }\n }\n\n var bidiRE = /[\\u0590-\\u05f4\\u0600-\\u06ff\\u0700-\\u08ac]/;\n var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;\n\n function BidiSpan(level, from, to) {\n this.level = level;\n this.from = from; this.to = to;\n }\n\n return function(str, direction) {\n var outerType = direction == \"ltr\" ? \"L\" : \"R\";\n\n if (str.length == 0 || direction == \"ltr\" && !bidiRE.test(str)) { return false }\n var len = str.length, types = [];\n for (var i = 0; i < len; ++i)\n { types.push(charType(str.charCodeAt(i))); }\n\n // W1. Examine each non-spacing mark (NSM) in the level run, and\n // change the type of the NSM to the type of the previous\n // character. If the NSM is at the start of the level run, it will\n // get the type of sor.\n for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {\n var type = types[i$1];\n if (type == \"m\") { types[i$1] = prev; }\n else { prev = type; }\n }\n\n // W2. Search backwards from each instance of a European number\n // until the first strong type (R, L, AL, or sor) is found. If an\n // AL is found, change the type of the European number to Arabic\n // number.\n // W3. Change all ALs to R.\n for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {\n var type$1 = types[i$2];\n if (type$1 == \"1\" && cur == \"r\") { types[i$2] = \"n\"; }\n else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == \"r\") { types[i$2] = \"R\"; } }\n }\n\n // W4. A single European separator between two European numbers\n // changes to a European number. A single common separator between\n // two numbers of the same type changes to that type.\n for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {\n var type$2 = types[i$3];\n if (type$2 == \"+\" && prev$1 == \"1\" && types[i$3+1] == \"1\") { types[i$3] = \"1\"; }\n else if (type$2 == \",\" && prev$1 == types[i$3+1] &&\n (prev$1 == \"1\" || prev$1 == \"n\")) { types[i$3] = prev$1; }\n prev$1 = type$2;\n }\n\n // W5. A sequence of European terminators adjacent to European\n // numbers changes to all European numbers.\n // W6. Otherwise, separators and terminators change to Other\n // Neutral.\n for (var i$4 = 0; i$4 < len; ++i$4) {\n var type$3 = types[i$4];\n if (type$3 == \",\") { types[i$4] = \"N\"; }\n else if (type$3 == \"%\") {\n var end = (void 0);\n for (end = i$4 + 1; end < len && types[end] == \"%\"; ++end) {}\n var replace = (i$4 && types[i$4-1] == \"!\") || (end < len && types[end] == \"1\") ? \"1\" : \"N\";\n for (var j = i$4; j < end; ++j) { types[j] = replace; }\n i$4 = end - 1;\n }\n }\n\n // W7. Search backwards from each instance of a European number\n // until the first strong type (R, L, or sor) is found. If an L is\n // found, then change the type of the European number to L.\n for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {\n var type$4 = types[i$5];\n if (cur$1 == \"L\" && type$4 == \"1\") { types[i$5] = \"L\"; }\n else if (isStrong.test(type$4)) { cur$1 = type$4; }\n }\n\n // N1. A sequence of neutrals takes the direction of the\n // surrounding strong text if the text on both sides has the same\n // direction. European and Arabic numbers act as if they were R in\n // terms of their influence on neutrals. Start-of-level-run (sor)\n // and end-of-level-run (eor) are used at level run boundaries.\n // N2. Any remaining neutrals take the embedding direction.\n for (var i$6 = 0; i$6 < len; ++i$6) {\n if (isNeutral.test(types[i$6])) {\n var end$1 = (void 0);\n for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}\n var before = (i$6 ? types[i$6-1] : outerType) == \"L\";\n var after = (end$1 < len ? types[end$1] : outerType) == \"L\";\n var replace$1 = before == after ? (before ? \"L\" : \"R\") : outerType;\n for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; }\n i$6 = end$1 - 1;\n }\n }\n\n // Here we depart from the documented algorithm, in order to avoid\n // building up an actual levels array. Since there are only three\n // levels (0, 1, 2) in an implementation that doesn't take\n // explicit embedding into account, we can build up the order on\n // the fly, without following the level-based algorithm.\n var order = [], m;\n for (var i$7 = 0; i$7 < len;) {\n if (countsAsLeft.test(types[i$7])) {\n var start = i$7;\n for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}\n order.push(new BidiSpan(0, start, i$7));\n } else {\n var pos = i$7, at = order.length;\n for (++i$7; i$7 < len && types[i$7] != \"L\"; ++i$7) {}\n for (var j$2 = pos; j$2 < i$7;) {\n if (countsAsNum.test(types[j$2])) {\n if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); }\n var nstart = j$2;\n for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}\n order.splice(at, 0, new BidiSpan(2, nstart, j$2));\n pos = j$2;\n } else { ++j$2; }\n }\n if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); }\n }\n }\n if (direction == \"ltr\") {\n if (order[0].level == 1 && (m = str.match(/^\\s+/))) {\n order[0].from = m[0].length;\n order.unshift(new BidiSpan(0, 0, m[0].length));\n }\n if (lst(order).level == 1 && (m = str.match(/\\s+$/))) {\n lst(order).to -= m[0].length;\n order.push(new BidiSpan(0, len - m[0].length, len));\n }\n }\n\n return direction == \"rtl\" ? order.reverse() : order\n }\n })();\n\n // Get the bidi ordering for the given line (and cache it). Returns\n // false for lines that are fully left-to-right, and an array of\n // BidiSpan objects otherwise.\n function getOrder(line, direction) {\n var order = line.order;\n if (order == null) { order = line.order = bidiOrdering(line.text, direction); }\n return order\n }\n\n // EVENT HANDLING\n\n // Lightweight event framework. on/off also work on DOM nodes,\n // registering native DOM handlers.\n\n var noHandlers = [];\n\n var on = function(emitter, type, f) {\n if (emitter.addEventListener) {\n emitter.addEventListener(type, f, false);\n } else if (emitter.attachEvent) {\n emitter.attachEvent(\"on\" + type, f);\n } else {\n var map = emitter._handlers || (emitter._handlers = {});\n map[type] = (map[type] || noHandlers).concat(f);\n }\n };\n\n function getHandlers(emitter, type) {\n return emitter._handlers && emitter._handlers[type] || noHandlers\n }\n\n function off(emitter, type, f) {\n if (emitter.removeEventListener) {\n emitter.removeEventListener(type, f, false);\n } else if (emitter.detachEvent) {\n emitter.detachEvent(\"on\" + type, f);\n } else {\n var map = emitter._handlers, arr = map && map[type];\n if (arr) {\n var index = indexOf(arr, f);\n if (index > -1)\n { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)); }\n }\n }\n }\n\n function signal(emitter, type /*, values...*/) {\n var handlers = getHandlers(emitter, type);\n if (!handlers.length) { return }\n var args = Array.prototype.slice.call(arguments, 2);\n for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); }\n }\n\n // The DOM events that CodeMirror handles can be overridden by\n // registering a (non-DOM) handler on the editor for the event name,\n // and preventDefault-ing the event in that handler.\n function signalDOMEvent(cm, e, override) {\n if (typeof e == \"string\")\n { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; }\n signal(cm, override || e.type, cm, e);\n return e_defaultPrevented(e) || e.codemirrorIgnore\n }\n\n function signalCursorActivity(cm) {\n var arr = cm._handlers && cm._handlers.cursorActivity;\n if (!arr) { return }\n var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);\n for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)\n { set.push(arr[i]); } }\n }\n\n function hasHandler(emitter, type) {\n return getHandlers(emitter, type).length > 0\n }\n\n // Add on and off methods to a constructor's prototype, to make\n // registering events on such objects more convenient.\n function eventMixin(ctor) {\n ctor.prototype.on = function(type, f) {on(this, type, f);};\n ctor.prototype.off = function(type, f) {off(this, type, f);};\n }\n\n // Due to the fact that we still support jurassic IE versions, some\n // compatibility wrappers are needed.\n\n function e_preventDefault(e) {\n if (e.preventDefault) { e.preventDefault(); }\n else { e.returnValue = false; }\n }\n function e_stopPropagation(e) {\n if (e.stopPropagation) { e.stopPropagation(); }\n else { e.cancelBubble = true; }\n }\n function e_defaultPrevented(e) {\n return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false\n }\n function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}\n\n function e_target(e) {return e.target || e.srcElement}\n function e_button(e) {\n var b = e.which;\n if (b == null) {\n if (e.button & 1) { b = 1; }\n else if (e.button & 2) { b = 3; }\n else if (e.button & 4) { b = 2; }\n }\n if (mac && e.ctrlKey && b == 1) { b = 3; }\n return b\n }\n\n // Detect drag-and-drop\n var dragAndDrop = function() {\n // There is *some* kind of drag-and-drop support in IE6-8, but I\n // couldn't get it to work yet.\n if (ie && ie_version < 9) { return false }\n var div = elt('div');\n return \"draggable\" in div || \"dragDrop\" in div\n }();\n\n var zwspSupported;\n function zeroWidthElement(measure) {\n if (zwspSupported == null) {\n var test = elt(\"span\", \"\\u200b\");\n removeChildrenAndAdd(measure, elt(\"span\", [test, document.createTextNode(\"x\")]));\n if (measure.firstChild.offsetHeight != 0)\n { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); }\n }\n var node = zwspSupported ? elt(\"span\", \"\\u200b\") :\n elt(\"span\", \"\\u00a0\", null, \"display: inline-block; width: 1px; margin-right: -1px\");\n node.setAttribute(\"cm-text\", \"\");\n return node\n }\n\n // Feature-detect IE's crummy client rect reporting for bidi text\n var badBidiRects;\n function hasBadBidiRects(measure) {\n if (badBidiRects != null) { return badBidiRects }\n var txt = removeChildrenAndAdd(measure, document.createTextNode(\"A\\u062eA\"));\n var r0 = range(txt, 0, 1).getBoundingClientRect();\n var r1 = range(txt, 1, 2).getBoundingClientRect();\n removeChildren(measure);\n if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)\n return badBidiRects = (r1.right - r0.right < 3)\n }\n\n // See if \"\".split is the broken IE version, if so, provide an\n // alternative way to split lines.\n var splitLinesAuto = \"\\n\\nb\".split(/\\n/).length != 3 ? function (string) {\n var pos = 0, result = [], l = string.length;\n while (pos <= l) {\n var nl = string.indexOf(\"\\n\", pos);\n if (nl == -1) { nl = string.length; }\n var line = string.slice(pos, string.charAt(nl - 1) == \"\\r\" ? nl - 1 : nl);\n var rt = line.indexOf(\"\\r\");\n if (rt != -1) {\n result.push(line.slice(0, rt));\n pos += rt + 1;\n } else {\n result.push(line);\n pos = nl + 1;\n }\n }\n return result\n } : function (string) { return string.split(/\\r\\n?|\\n/); };\n\n var hasSelection = window.getSelection ? function (te) {\n try { return te.selectionStart != te.selectionEnd }\n catch(e) { return false }\n } : function (te) {\n var range;\n try {range = te.ownerDocument.selection.createRange();}\n catch(e) {}\n if (!range || range.parentElement() != te) { return false }\n return range.compareEndPoints(\"StartToEnd\", range) != 0\n };\n\n var hasCopyEvent = (function () {\n var e = elt(\"div\");\n if (\"oncopy\" in e) { return true }\n e.setAttribute(\"oncopy\", \"return;\");\n return typeof e.oncopy == \"function\"\n })();\n\n var badZoomedRects = null;\n function hasBadZoomedRects(measure) {\n if (badZoomedRects != null) { return badZoomedRects }\n var node = removeChildrenAndAdd(measure, elt(\"span\", \"x\"));\n var normal = node.getBoundingClientRect();\n var fromRange = range(node, 0, 1).getBoundingClientRect();\n return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1\n }\n\n // Known modes, by name and by MIME\n var modes = {}, mimeModes = {};\n\n // Extra arguments are stored as the mode's dependencies, which is\n // used by (legacy) mechanisms like loadmode.js to automatically\n // load a mode. (Preferred mechanism is the require/define calls.)\n function defineMode(name, mode) {\n if (arguments.length > 2)\n { mode.dependencies = Array.prototype.slice.call(arguments, 2); }\n modes[name] = mode;\n }\n\n function defineMIME(mime, spec) {\n mimeModes[mime] = spec;\n }\n\n // Given a MIME type, a {name, ...options} config object, or a name\n // string, return a mode config object.\n function resolveMode(spec) {\n if (typeof spec == \"string\" && mimeModes.hasOwnProperty(spec)) {\n spec = mimeModes[spec];\n } else if (spec && typeof spec.name == \"string\" && mimeModes.hasOwnProperty(spec.name)) {\n var found = mimeModes[spec.name];\n if (typeof found == \"string\") { found = {name: found}; }\n spec = createObj(found, spec);\n spec.name = found.name;\n } else if (typeof spec == \"string\" && /^[\\w\\-]+\\/[\\w\\-]+\\+xml$/.test(spec)) {\n return resolveMode(\"application/xml\")\n } else if (typeof spec == \"string\" && /^[\\w\\-]+\\/[\\w\\-]+\\+json$/.test(spec)) {\n return resolveMode(\"application/json\")\n }\n if (typeof spec == \"string\") { return {name: spec} }\n else { return spec || {name: \"null\"} }\n }\n\n // Given a mode spec (anything that resolveMode accepts), find and\n // initialize an actual mode object.\n function getMode(options, spec) {\n spec = resolveMode(spec);\n var mfactory = modes[spec.name];\n if (!mfactory) { return getMode(options, \"text/plain\") }\n var modeObj = mfactory(options, spec);\n if (modeExtensions.hasOwnProperty(spec.name)) {\n var exts = modeExtensions[spec.name];\n for (var prop in exts) {\n if (!exts.hasOwnProperty(prop)) { continue }\n if (modeObj.hasOwnProperty(prop)) { modeObj[\"_\" + prop] = modeObj[prop]; }\n modeObj[prop] = exts[prop];\n }\n }\n modeObj.name = spec.name;\n if (spec.helperType) { modeObj.helperType = spec.helperType; }\n if (spec.modeProps) { for (var prop$1 in spec.modeProps)\n { modeObj[prop$1] = spec.modeProps[prop$1]; } }\n\n return modeObj\n }\n\n // This can be used to attach properties to mode objects from\n // outside the actual mode definition.\n var modeExtensions = {};\n function extendMode(mode, properties) {\n var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});\n copyObj(properties, exts);\n }\n\n function copyState(mode, state) {\n if (state === true) { return state }\n if (mode.copyState) { return mode.copyState(state) }\n var nstate = {};\n for (var n in state) {\n var val = state[n];\n if (val instanceof Array) { val = val.concat([]); }\n nstate[n] = val;\n }\n return nstate\n }\n\n // Given a mode and a state (for that mode), find the inner mode and\n // state at the position that the state refers to.\n function innerMode(mode, state) {\n var info;\n while (mode.innerMode) {\n info = mode.innerMode(state);\n if (!info || info.mode == mode) { break }\n state = info.state;\n mode = info.mode;\n }\n return info || {mode: mode, state: state}\n }\n\n function startState(mode, a1, a2) {\n return mode.startState ? mode.startState(a1, a2) : true\n }\n\n // STRING STREAM\n\n // Fed to the mode parsers, provides helper functions to make\n // parsers more succinct.\n\n var StringStream = function(string, tabSize, lineOracle) {\n this.pos = this.start = 0;\n this.string = string;\n this.tabSize = tabSize || 8;\n this.lastColumnPos = this.lastColumnValue = 0;\n this.lineStart = 0;\n this.lineOracle = lineOracle;\n };\n\n StringStream.prototype.eol = function () {return this.pos >= this.string.length};\n StringStream.prototype.sol = function () {return this.pos == this.lineStart};\n StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};\n StringStream.prototype.next = function () {\n if (this.pos < this.string.length)\n { return this.string.charAt(this.pos++) }\n };\n StringStream.prototype.eat = function (match) {\n var ch = this.string.charAt(this.pos);\n var ok;\n if (typeof match == \"string\") { ok = ch == match; }\n else { ok = ch && (match.test ? match.test(ch) : match(ch)); }\n if (ok) {++this.pos; return ch}\n };\n StringStream.prototype.eatWhile = function (match) {\n var start = this.pos;\n while (this.eat(match)){}\n return this.pos > start\n };\n StringStream.prototype.eatSpace = function () {\n var start = this.pos;\n while (/[\\s\\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; }\n return this.pos > start\n };\n StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};\n StringStream.prototype.skipTo = function (ch) {\n var found = this.string.indexOf(ch, this.pos);\n if (found > -1) {this.pos = found; return true}\n };\n StringStream.prototype.backUp = function (n) {this.pos -= n;};\n StringStream.prototype.column = function () {\n if (this.lastColumnPos < this.start) {\n this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);\n this.lastColumnPos = this.start;\n }\n return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)\n };\n StringStream.prototype.indentation = function () {\n return countColumn(this.string, null, this.tabSize) -\n (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)\n };\n StringStream.prototype.match = function (pattern, consume, caseInsensitive) {\n if (typeof pattern == \"string\") {\n var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };\n var substr = this.string.substr(this.pos, pattern.length);\n if (cased(substr) == cased(pattern)) {\n if (consume !== false) { this.pos += pattern.length; }\n return true\n }\n } else {\n var match = this.string.slice(this.pos).match(pattern);\n if (match && match.index > 0) { return null }\n if (match && consume !== false) { this.pos += match[0].length; }\n return match\n }\n };\n StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};\n StringStream.prototype.hideFirstChars = function (n, inner) {\n this.lineStart += n;\n try { return inner() }\n finally { this.lineStart -= n; }\n };\n StringStream.prototype.lookAhead = function (n) {\n var oracle = this.lineOracle;\n return oracle && oracle.lookAhead(n)\n };\n StringStream.prototype.baseToken = function () {\n var oracle = this.lineOracle;\n return oracle && oracle.baseToken(this.pos)\n };\n\n // Find the line object corresponding to the given line number.\n function getLine(doc, n) {\n n -= doc.first;\n if (n < 0 || n >= doc.size) { throw new Error(\"There is no line \" + (n + doc.first) + \" in the document.\") }\n var chunk = doc;\n while (!chunk.lines) {\n for (var i = 0;; ++i) {\n var child = chunk.children[i], sz = child.chunkSize();\n if (n < sz) { chunk = child; break }\n n -= sz;\n }\n }\n return chunk.lines[n]\n }\n\n // Get the part of a document between two positions, as an array of\n // strings.\n function getBetween(doc, start, end) {\n var out = [], n = start.line;\n doc.iter(start.line, end.line + 1, function (line) {\n var text = line.text;\n if (n == end.line) { text = text.slice(0, end.ch); }\n if (n == start.line) { text = text.slice(start.ch); }\n out.push(text);\n ++n;\n });\n return out\n }\n // Get the lines between from and to, as array of strings.\n function getLines(doc, from, to) {\n var out = [];\n doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value\n return out\n }\n\n // Update the height of a line, propagating the height change\n // upwards to parent nodes.\n function updateLineHeight(line, height) {\n var diff = height - line.height;\n if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } }\n }\n\n // Given a line object, find its line number by walking up through\n // its parent links.\n function lineNo(line) {\n if (line.parent == null) { return null }\n var cur = line.parent, no = indexOf(cur.lines, line);\n for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {\n for (var i = 0;; ++i) {\n if (chunk.children[i] == cur) { break }\n no += chunk.children[i].chunkSize();\n }\n }\n return no + cur.first\n }\n\n // Find the line at the given vertical position, using the height\n // information in the document tree.\n function lineAtHeight(chunk, h) {\n var n = chunk.first;\n outer: do {\n for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {\n var child = chunk.children[i$1], ch = child.height;\n if (h < ch) { chunk = child; continue outer }\n h -= ch;\n n += child.chunkSize();\n }\n return n\n } while (!chunk.lines)\n var i = 0;\n for (; i < chunk.lines.length; ++i) {\n var line = chunk.lines[i], lh = line.height;\n if (h < lh) { break }\n h -= lh;\n }\n return n + i\n }\n\n function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}\n\n function lineNumberFor(options, i) {\n return String(options.lineNumberFormatter(i + options.firstLineNumber))\n }\n\n // A Pos instance represents a position within the text.\n function Pos(line, ch, sticky) {\n if ( sticky === void 0 ) sticky = null;\n\n if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }\n this.line = line;\n this.ch = ch;\n this.sticky = sticky;\n }\n\n // Compare two positions, return 0 if they are the same, a negative\n // number when a is less, and a positive number otherwise.\n function cmp(a, b) { return a.line - b.line || a.ch - b.ch }\n\n function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }\n\n function copyPos(x) {return Pos(x.line, x.ch)}\n function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }\n function minPos(a, b) { return cmp(a, b) < 0 ? a : b }\n\n // Most of the external API clips given positions to make sure they\n // actually exist within the document.\n function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}\n function clipPos(doc, pos) {\n if (pos.line < doc.first) { return Pos(doc.first, 0) }\n var last = doc.first + doc.size - 1;\n if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }\n return clipToLen(pos, getLine(doc, pos.line).text.length)\n }\n function clipToLen(pos, linelen) {\n var ch = pos.ch;\n if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }\n else if (ch < 0) { return Pos(pos.line, 0) }\n else { return pos }\n }\n function clipPosArray(doc, array) {\n var out = [];\n for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); }\n return out\n }\n\n var SavedContext = function(state, lookAhead) {\n this.state = state;\n this.lookAhead = lookAhead;\n };\n\n var Context = function(doc, state, line, lookAhead) {\n this.state = state;\n this.doc = doc;\n this.line = line;\n this.maxLookAhead = lookAhead || 0;\n this.baseTokens = null;\n this.baseTokenPos = 1;\n };\n\n Context.prototype.lookAhead = function (n) {\n var line = this.doc.getLine(this.line + n);\n if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; }\n return line\n };\n\n Context.prototype.baseToken = function (n) {\n if (!this.baseTokens) { return null }\n while (this.baseTokens[this.baseTokenPos] <= n)\n { this.baseTokenPos += 2; }\n var type = this.baseTokens[this.baseTokenPos + 1];\n return {type: type && type.replace(/( |^)overlay .*/, \"\"),\n size: this.baseTokens[this.baseTokenPos] - n}\n };\n\n Context.prototype.nextLine = function () {\n this.line++;\n if (this.maxLookAhead > 0) { this.maxLookAhead--; }\n };\n\n Context.fromSaved = function (doc, saved, line) {\n if (saved instanceof SavedContext)\n { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }\n else\n { return new Context(doc, copyState(doc.mode, saved), line) }\n };\n\n Context.prototype.save = function (copy) {\n var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state;\n return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state\n };\n\n\n // Compute a style array (an array starting with a mode generation\n // -- for invalidation -- followed by pairs of end positions and\n // style strings), which is used to highlight the tokens on the\n // line.\n function highlightLine(cm, line, context, forceToEnd) {\n // A styles array always starts with a number identifying the\n // mode/overlays that it is based on (for easy invalidation).\n var st = [cm.state.modeGen], lineClasses = {};\n // Compute the base array of styles\n runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },\n lineClasses, forceToEnd);\n var state = context.state;\n\n // Run overlays, adjust style array.\n var loop = function ( o ) {\n context.baseTokens = st;\n var overlay = cm.state.overlays[o], i = 1, at = 0;\n context.state = true;\n runMode(cm, line.text, overlay.mode, context, function (end, style) {\n var start = i;\n // Ensure there's a token end at the current position, and that i points at it\n while (at < end) {\n var i_end = st[i];\n if (i_end > end)\n { st.splice(i, 1, end, st[i+1], i_end); }\n i += 2;\n at = Math.min(end, i_end);\n }\n if (!style) { return }\n if (overlay.opaque) {\n st.splice(start, i - start, end, \"overlay \" + style);\n i = start + 2;\n } else {\n for (; start < i; start += 2) {\n var cur = st[start+1];\n st[start+1] = (cur ? cur + \" \" : \"\") + \"overlay \" + style;\n }\n }\n }, lineClasses);\n context.state = state;\n context.baseTokens = null;\n context.baseTokenPos = 1;\n };\n\n for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );\n\n return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}\n }\n\n function getLineStyles(cm, line, updateFrontier) {\n if (!line.styles || line.styles[0] != cm.state.modeGen) {\n var context = getContextBefore(cm, lineNo(line));\n var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state);\n var result = highlightLine(cm, line, context);\n if (resetState) { context.state = resetState; }\n line.stateAfter = context.save(!resetState);\n line.styles = result.styles;\n if (result.classes) { line.styleClasses = result.classes; }\n else if (line.styleClasses) { line.styleClasses = null; }\n if (updateFrontier === cm.doc.highlightFrontier)\n { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); }\n }\n return line.styles\n }\n\n function getContextBefore(cm, n, precise) {\n var doc = cm.doc, display = cm.display;\n if (!doc.mode.startState) { return new Context(doc, true, n) }\n var start = findStartLine(cm, n, precise);\n var saved = start > doc.first && getLine(doc, start - 1).stateAfter;\n var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start);\n\n doc.iter(start, n, function (line) {\n processLine(cm, line.text, context);\n var pos = context.line;\n line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null;\n context.nextLine();\n });\n if (precise) { doc.modeFrontier = context.line; }\n return context\n }\n\n // Lightweight form of highlight -- proceed over this line and\n // update state, but don't save a style array. Used for lines that\n // aren't currently visible.\n function processLine(cm, text, context, startAt) {\n var mode = cm.doc.mode;\n var stream = new StringStream(text, cm.options.tabSize, context);\n stream.start = stream.pos = startAt || 0;\n if (text == \"\") { callBlankLine(mode, context.state); }\n while (!stream.eol()) {\n readToken(mode, stream, context.state);\n stream.start = stream.pos;\n }\n }\n\n function callBlankLine(mode, state) {\n if (mode.blankLine) { return mode.blankLine(state) }\n if (!mode.innerMode) { return }\n var inner = innerMode(mode, state);\n if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }\n }\n\n function readToken(mode, stream, state, inner) {\n for (var i = 0; i < 10; i++) {\n if (inner) { inner[0] = innerMode(mode, state).mode; }\n var style = mode.token(stream, state);\n if (stream.pos > stream.start) { return style }\n }\n throw new Error(\"Mode \" + mode.name + \" failed to advance stream.\")\n }\n\n var Token = function(stream, type, state) {\n this.start = stream.start; this.end = stream.pos;\n this.string = stream.current();\n this.type = type || null;\n this.state = state;\n };\n\n // Utility for getTokenAt and getLineTokens\n function takeToken(cm, pos, precise, asArray) {\n var doc = cm.doc, mode = doc.mode, style;\n pos = clipPos(doc, pos);\n var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);\n var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;\n if (asArray) { tokens = []; }\n while ((asArray || stream.pos < pos.ch) && !stream.eol()) {\n stream.start = stream.pos;\n style = readToken(mode, stream, context.state);\n if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); }\n }\n return asArray ? tokens : new Token(stream, style, context.state)\n }\n\n function extractLineClasses(type, output) {\n if (type) { for (;;) {\n var lineClass = type.match(/(?:^|\\s+)line-(background-)?(\\S+)/);\n if (!lineClass) { break }\n type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);\n var prop = lineClass[1] ? \"bgClass\" : \"textClass\";\n if (output[prop] == null)\n { output[prop] = lineClass[2]; }\n else if (!(new RegExp(\"(?:^|\\s)\" + lineClass[2] + \"(?:$|\\s)\")).test(output[prop]))\n { output[prop] += \" \" + lineClass[2]; }\n } }\n return type\n }\n\n // Run the given mode's parser over a line, calling f for each token.\n function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {\n var flattenSpans = mode.flattenSpans;\n if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; }\n var curStart = 0, curStyle = null;\n var stream = new StringStream(text, cm.options.tabSize, context), style;\n var inner = cm.options.addModeClass && [null];\n if (text == \"\") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); }\n while (!stream.eol()) {\n if (stream.pos > cm.options.maxHighlightLength) {\n flattenSpans = false;\n if (forceToEnd) { processLine(cm, text, context, stream.pos); }\n stream.pos = text.length;\n style = null;\n } else {\n style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses);\n }\n if (inner) {\n var mName = inner[0].name;\n if (mName) { style = \"m-\" + (style ? mName + \" \" + style : mName); }\n }\n if (!flattenSpans || curStyle != style) {\n while (curStart < stream.start) {\n curStart = Math.min(stream.start, curStart + 5000);\n f(curStart, curStyle);\n }\n curStyle = style;\n }\n stream.start = stream.pos;\n }\n while (curStart < stream.pos) {\n // Webkit seems to refuse to render text nodes longer than 57444\n // characters, and returns inaccurate measurements in nodes\n // starting around 5000 chars.\n var pos = Math.min(stream.pos, curStart + 5000);\n f(pos, curStyle);\n curStart = pos;\n }\n }\n\n // Finds the line to start with when starting a parse. Tries to\n // find a line with a stateAfter, so that it can start with a\n // valid state. If that fails, it returns the line with the\n // smallest indentation, which tends to need the least context to\n // parse correctly.\n function findStartLine(cm, n, precise) {\n var minindent, minline, doc = cm.doc;\n var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);\n for (var search = n; search > lim; --search) {\n if (search <= doc.first) { return doc.first }\n var line = getLine(doc, search - 1), after = line.stateAfter;\n if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))\n { return search }\n var indented = countColumn(line.text, null, cm.options.tabSize);\n if (minline == null || minindent > indented) {\n minline = search - 1;\n minindent = indented;\n }\n }\n return minline\n }\n\n function retreatFrontier(doc, n) {\n doc.modeFrontier = Math.min(doc.modeFrontier, n);\n if (doc.highlightFrontier < n - 10) { return }\n var start = doc.first;\n for (var line = n - 1; line > start; line--) {\n var saved = getLine(doc, line).stateAfter;\n // change is on 3\n // state on line 1 looked ahead 2 -- so saw 3\n // test 1 + 2 < 3 should cover this\n if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {\n start = line + 1;\n break\n }\n }\n doc.highlightFrontier = Math.min(doc.highlightFrontier, start);\n }\n\n // Optimize some code when these features are not used.\n var sawReadOnlySpans = false, sawCollapsedSpans = false;\n\n function seeReadOnlySpans() {\n sawReadOnlySpans = true;\n }\n\n function seeCollapsedSpans() {\n sawCollapsedSpans = true;\n }\n\n // TEXTMARKER SPANS\n\n function MarkedSpan(marker, from, to) {\n this.marker = marker;\n this.from = from; this.to = to;\n }\n\n // Search an array of spans for a span matching the given marker.\n function getMarkedSpanFor(spans, marker) {\n if (spans) { for (var i = 0; i < spans.length; ++i) {\n var span = spans[i];\n if (span.marker == marker) { return span }\n } }\n }\n // Remove a span from an array, returning undefined if no spans are\n // left (we don't store arrays for lines without spans).\n function removeMarkedSpan(spans, span) {\n var r;\n for (var i = 0; i < spans.length; ++i)\n { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } }\n return r\n }\n // Add a span to a line.\n function addMarkedSpan(line, span) {\n line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];\n span.marker.attachLine(line);\n }\n\n // Used for the algorithm that adjusts markers for a change in the\n // document. These functions cut an array of spans at a given\n // character position, returning an array of remaining chunks (or\n // undefined if nothing remains).\n function markedSpansBefore(old, startCh, isInsert) {\n var nw;\n if (old) { for (var i = 0; i < old.length; ++i) {\n var span = old[i], marker = span.marker;\n var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);\n if (startsBefore || span.from == startCh && marker.type == \"bookmark\" && (!isInsert || !span.marker.insertLeft)) {\n var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)\n ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));\n }\n } }\n return nw\n }\n function markedSpansAfter(old, endCh, isInsert) {\n var nw;\n if (old) { for (var i = 0; i < old.length; ++i) {\n var span = old[i], marker = span.marker;\n var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);\n if (endsAfter || span.from == endCh && marker.type == \"bookmark\" && (!isInsert || span.marker.insertLeft)) {\n var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)\n ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,\n span.to == null ? null : span.to - endCh));\n }\n } }\n return nw\n }\n\n // Given a change object, compute the new set of marker spans that\n // cover the line in which the change took place. Removes spans\n // entirely within the change, reconnects spans belonging to the\n // same marker that appear on both sides of the change, and cuts off\n // spans partially within the change. Returns an array of span\n // arrays with one element for each line in (after) the change.\n function stretchSpansOverChange(doc, change) {\n if (change.full) { return null }\n var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;\n var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;\n if (!oldFirst && !oldLast) { return null }\n\n var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;\n // Get the spans that 'stick out' on both sides\n var first = markedSpansBefore(oldFirst, startCh, isInsert);\n var last = markedSpansAfter(oldLast, endCh, isInsert);\n\n // Next, merge those two ends\n var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);\n if (first) {\n // Fix up .to properties of first\n for (var i = 0; i < first.length; ++i) {\n var span = first[i];\n if (span.to == null) {\n var found = getMarkedSpanFor(last, span.marker);\n if (!found) { span.to = startCh; }\n else if (sameLine) { span.to = found.to == null ? null : found.to + offset; }\n }\n }\n }\n if (last) {\n // Fix up .from in last (or move them into first in case of sameLine)\n for (var i$1 = 0; i$1 < last.length; ++i$1) {\n var span$1 = last[i$1];\n if (span$1.to != null) { span$1.to += offset; }\n if (span$1.from == null) {\n var found$1 = getMarkedSpanFor(first, span$1.marker);\n if (!found$1) {\n span$1.from = offset;\n if (sameLine) { (first || (first = [])).push(span$1); }\n }\n } else {\n span$1.from += offset;\n if (sameLine) { (first || (first = [])).push(span$1); }\n }\n }\n }\n // Make sure we didn't create any zero-length spans\n if (first) { first = clearEmptySpans(first); }\n if (last && last != first) { last = clearEmptySpans(last); }\n\n var newMarkers = [first];\n if (!sameLine) {\n // Fill gap with whole-line-spans\n var gap = change.text.length - 2, gapMarkers;\n if (gap > 0 && first)\n { for (var i$2 = 0; i$2 < first.length; ++i$2)\n { if (first[i$2].to == null)\n { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } }\n for (var i$3 = 0; i$3 < gap; ++i$3)\n { newMarkers.push(gapMarkers); }\n newMarkers.push(last);\n }\n return newMarkers\n }\n\n // Remove spans that are empty and don't have a clearWhenEmpty\n // option of false.\n function clearEmptySpans(spans) {\n for (var i = 0; i < spans.length; ++i) {\n var span = spans[i];\n if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)\n { spans.splice(i--, 1); }\n }\n if (!spans.length) { return null }\n return spans\n }\n\n // Used to 'clip' out readOnly ranges when making a change.\n function removeReadOnlyRanges(doc, from, to) {\n var markers = null;\n doc.iter(from.line, to.line + 1, function (line) {\n if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {\n var mark = line.markedSpans[i].marker;\n if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))\n { (markers || (markers = [])).push(mark); }\n } }\n });\n if (!markers) { return null }\n var parts = [{from: from, to: to}];\n for (var i = 0; i < markers.length; ++i) {\n var mk = markers[i], m = mk.find(0);\n for (var j = 0; j < parts.length; ++j) {\n var p = parts[j];\n if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }\n var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);\n if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)\n { newParts.push({from: p.from, to: m.from}); }\n if (dto > 0 || !mk.inclusiveRight && !dto)\n { newParts.push({from: m.to, to: p.to}); }\n parts.splice.apply(parts, newParts);\n j += newParts.length - 3;\n }\n }\n return parts\n }\n\n // Connect or disconnect spans from a line.\n function detachMarkedSpans(line) {\n var spans = line.markedSpans;\n if (!spans) { return }\n for (var i = 0; i < spans.length; ++i)\n { spans[i].marker.detachLine(line); }\n line.markedSpans = null;\n }\n function attachMarkedSpans(line, spans) {\n if (!spans) { return }\n for (var i = 0; i < spans.length; ++i)\n { spans[i].marker.attachLine(line); }\n line.markedSpans = spans;\n }\n\n // Helpers used when computing which overlapping collapsed span\n // counts as the larger one.\n function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }\n function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }\n\n // Returns a number indicating which of two overlapping collapsed\n // spans is larger (and thus includes the other). Falls back to\n // comparing ids when the spans cover exactly the same range.\n function compareCollapsedMarkers(a, b) {\n var lenDiff = a.lines.length - b.lines.length;\n if (lenDiff != 0) { return lenDiff }\n var aPos = a.find(), bPos = b.find();\n var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);\n if (fromCmp) { return -fromCmp }\n var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);\n if (toCmp) { return toCmp }\n return b.id - a.id\n }\n\n // Find out whether a line ends or starts in a collapsed span. If\n // so, return the marker for that span.\n function collapsedSpanAtSide(line, start) {\n var sps = sawCollapsedSpans && line.markedSpans, found;\n if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {\n sp = sps[i];\n if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&\n (!found || compareCollapsedMarkers(found, sp.marker) < 0))\n { found = sp.marker; }\n } }\n return found\n }\n function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }\n function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }\n\n function collapsedSpanAround(line, ch) {\n var sps = sawCollapsedSpans && line.markedSpans, found;\n if (sps) { for (var i = 0; i < sps.length; ++i) {\n var sp = sps[i];\n if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) &&\n (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; }\n } }\n return found\n }\n\n // Test whether there exists a collapsed span that partially\n // overlaps (covers the start or end, but not both) of a new span.\n // Such overlap is not allowed.\n function conflictingCollapsedRange(doc, lineNo, from, to, marker) {\n var line = getLine(doc, lineNo);\n var sps = sawCollapsedSpans && line.markedSpans;\n if (sps) { for (var i = 0; i < sps.length; ++i) {\n var sp = sps[i];\n if (!sp.marker.collapsed) { continue }\n var found = sp.marker.find(0);\n var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);\n var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);\n if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }\n if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||\n fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))\n { return true }\n } }\n }\n\n // A visual line is a line as drawn on the screen. Folding, for\n // example, can cause multiple logical lines to appear on the same\n // visual line. This finds the start of the visual line that the\n // given line is part of (usually that is the line itself).\n function visualLine(line) {\n var merged;\n while (merged = collapsedSpanAtStart(line))\n { line = merged.find(-1, true).line; }\n return line\n }\n\n function visualLineEnd(line) {\n var merged;\n while (merged = collapsedSpanAtEnd(line))\n { line = merged.find(1, true).line; }\n return line\n }\n\n // Returns an array of logical lines that continue the visual line\n // started by the argument, or undefined if there are no such lines.\n function visualLineContinued(line) {\n var merged, lines;\n while (merged = collapsedSpanAtEnd(line)) {\n line = merged.find(1, true).line\n ;(lines || (lines = [])).push(line);\n }\n return lines\n }\n\n // Get the line number of the start of the visual line that the\n // given line number is part of.\n function visualLineNo(doc, lineN) {\n var line = getLine(doc, lineN), vis = visualLine(line);\n if (line == vis) { return lineN }\n return lineNo(vis)\n }\n\n // Get the line number of the start of the next visual line after\n // the given line.\n function visualLineEndNo(doc, lineN) {\n if (lineN > doc.lastLine()) { return lineN }\n var line = getLine(doc, lineN), merged;\n if (!lineIsHidden(doc, line)) { return lineN }\n while (merged = collapsedSpanAtEnd(line))\n { line = merged.find(1, true).line; }\n return lineNo(line) + 1\n }\n\n // Compute whether a line is hidden. Lines count as hidden when they\n // are part of a visual line that starts with another line, or when\n // they are entirely covered by collapsed, non-widget span.\n function lineIsHidden(doc, line) {\n var sps = sawCollapsedSpans && line.markedSpans;\n if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {\n sp = sps[i];\n if (!sp.marker.collapsed) { continue }\n if (sp.from == null) { return true }\n if (sp.marker.widgetNode) { continue }\n if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))\n { return true }\n } }\n }\n function lineIsHiddenInner(doc, line, span) {\n if (span.to == null) {\n var end = span.marker.find(1, true);\n return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))\n }\n if (span.marker.inclusiveRight && span.to == line.text.length)\n { return true }\n for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {\n sp = line.markedSpans[i];\n if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&\n (sp.to == null || sp.to != span.from) &&\n (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&\n lineIsHiddenInner(doc, line, sp)) { return true }\n }\n }\n\n // Find the height above the given line.\n function heightAtLine(lineObj) {\n lineObj = visualLine(lineObj);\n\n var h = 0, chunk = lineObj.parent;\n for (var i = 0; i < chunk.lines.length; ++i) {\n var line = chunk.lines[i];\n if (line == lineObj) { break }\n else { h += line.height; }\n }\n for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {\n for (var i$1 = 0; i$1 < p.children.length; ++i$1) {\n var cur = p.children[i$1];\n if (cur == chunk) { break }\n else { h += cur.height; }\n }\n }\n return h\n }\n\n // Compute the character length of a line, taking into account\n // collapsed ranges (see markText) that might hide parts, and join\n // other lines onto it.\n function lineLength(line) {\n if (line.height == 0) { return 0 }\n var len = line.text.length, merged, cur = line;\n while (merged = collapsedSpanAtStart(cur)) {\n var found = merged.find(0, true);\n cur = found.from.line;\n len += found.from.ch - found.to.ch;\n }\n cur = line;\n while (merged = collapsedSpanAtEnd(cur)) {\n var found$1 = merged.find(0, true);\n len -= cur.text.length - found$1.from.ch;\n cur = found$1.to.line;\n len += cur.text.length - found$1.to.ch;\n }\n return len\n }\n\n // Find the longest line in the document.\n function findMaxLine(cm) {\n var d = cm.display, doc = cm.doc;\n d.maxLine = getLine(doc, doc.first);\n d.maxLineLength = lineLength(d.maxLine);\n d.maxLineChanged = true;\n doc.iter(function (line) {\n var len = lineLength(line);\n if (len > d.maxLineLength) {\n d.maxLineLength = len;\n d.maxLine = line;\n }\n });\n }\n\n // LINE DATA STRUCTURE\n\n // Line objects. These hold state related to a line, including\n // highlighting info (the styles array).\n var Line = function(text, markedSpans, estimateHeight) {\n this.text = text;\n attachMarkedSpans(this, markedSpans);\n this.height = estimateHeight ? estimateHeight(this) : 1;\n };\n\n Line.prototype.lineNo = function () { return lineNo(this) };\n eventMixin(Line);\n\n // Change the content (text, markers) of a line. Automatically\n // invalidates cached information and tries to re-estimate the\n // line's height.\n function updateLine(line, text, markedSpans, estimateHeight) {\n line.text = text;\n if (line.stateAfter) { line.stateAfter = null; }\n if (line.styles) { line.styles = null; }\n if (line.order != null) { line.order = null; }\n detachMarkedSpans(line);\n attachMarkedSpans(line, markedSpans);\n var estHeight = estimateHeight ? estimateHeight(line) : 1;\n if (estHeight != line.height) { updateLineHeight(line, estHeight); }\n }\n\n // Detach a line from the document tree and its markers.\n function cleanUpLine(line) {\n line.parent = null;\n detachMarkedSpans(line);\n }\n\n // Convert a style as returned by a mode (either null, or a string\n // containing one or more styles) to a CSS style. This is cached,\n // and also looks for line-wide styles.\n var styleToClassCache = {}, styleToClassCacheWithMode = {};\n function interpretTokenStyle(style, options) {\n if (!style || /^\\s*$/.test(style)) { return null }\n var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;\n return cache[style] ||\n (cache[style] = style.replace(/\\S+/g, \"cm-$&\"))\n }\n\n // Render the DOM representation of the text of a line. Also builds\n // up a 'line map', which points at the DOM nodes that represent\n // specific stretches of text, and is used by the measuring code.\n // The returned object contains the DOM node, this map, and\n // information about line-wide styles that were set by the mode.\n function buildLineContent(cm, lineView) {\n // The padding-right forces the element to have a 'border', which\n // is needed on Webkit to be able to get line-level bounding\n // rectangles for it (in measureChar).\n var content = eltP(\"span\", null, null, webkit ? \"padding-right: .1px\" : null);\n var builder = {pre: eltP(\"pre\", [content], \"CodeMirror-line\"), content: content,\n col: 0, pos: 0, cm: cm,\n trailingSpace: false,\n splitSpaces: cm.getOption(\"lineWrapping\")};\n lineView.measure = {};\n\n // Iterate over the logical lines that make up this visual line.\n for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {\n var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0);\n builder.pos = 0;\n builder.addToken = buildToken;\n // Optionally wire in some hacks into the token-rendering\n // algorithm, to deal with browser quirks.\n if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))\n { builder.addToken = buildTokenBadBidi(builder.addToken, order); }\n builder.map = [];\n var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);\n insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));\n if (line.styleClasses) {\n if (line.styleClasses.bgClass)\n { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || \"\"); }\n if (line.styleClasses.textClass)\n { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || \"\"); }\n }\n\n // Ensure at least a single node is present, for measuring.\n if (builder.map.length == 0)\n { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); }\n\n // Store the map and a cache object for the current logical line\n if (i == 0) {\n lineView.measure.map = builder.map;\n lineView.measure.cache = {};\n } else {\n (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)\n ;(lineView.measure.caches || (lineView.measure.caches = [])).push({});\n }\n }\n\n // See issue #2901\n if (webkit) {\n var last = builder.content.lastChild;\n if (/\\bcm-tab\\b/.test(last.className) || (last.querySelector && last.querySelector(\".cm-tab\")))\n { builder.content.className = \"cm-tab-wrap-hack\"; }\n }\n\n signal(cm, \"renderLine\", cm, lineView.line, builder.pre);\n if (builder.pre.className)\n { builder.textClass = joinClasses(builder.pre.className, builder.textClass || \"\"); }\n\n return builder\n }\n\n function defaultSpecialCharPlaceholder(ch) {\n var token = elt(\"span\", \"\\u2022\", \"cm-invalidchar\");\n token.title = \"\\\\u\" + ch.charCodeAt(0).toString(16);\n token.setAttribute(\"aria-label\", token.title);\n return token\n }\n\n // Build up the DOM representation for a single token, and add it to\n // the line map. Takes care to render special characters separately.\n function buildToken(builder, text, style, startStyle, endStyle, css, attributes) {\n if (!text) { return }\n var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text;\n var special = builder.cm.state.specialChars, mustWrap = false;\n var content;\n if (!special.test(text)) {\n builder.col += text.length;\n content = document.createTextNode(displayText);\n builder.map.push(builder.pos, builder.pos + text.length, content);\n if (ie && ie_version < 9) { mustWrap = true; }\n builder.pos += text.length;\n } else {\n content = document.createDocumentFragment();\n var pos = 0;\n while (true) {\n special.lastIndex = pos;\n var m = special.exec(text);\n var skipped = m ? m.index - pos : text.length - pos;\n if (skipped) {\n var txt = document.createTextNode(displayText.slice(pos, pos + skipped));\n if (ie && ie_version < 9) { content.appendChild(elt(\"span\", [txt])); }\n else { content.appendChild(txt); }\n builder.map.push(builder.pos, builder.pos + skipped, txt);\n builder.col += skipped;\n builder.pos += skipped;\n }\n if (!m) { break }\n pos += skipped + 1;\n var txt$1 = (void 0);\n if (m[0] == \"\\t\") {\n var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;\n txt$1 = content.appendChild(elt(\"span\", spaceStr(tabWidth), \"cm-tab\"));\n txt$1.setAttribute(\"role\", \"presentation\");\n txt$1.setAttribute(\"cm-text\", \"\\t\");\n builder.col += tabWidth;\n } else if (m[0] == \"\\r\" || m[0] == \"\\n\") {\n txt$1 = content.appendChild(elt(\"span\", m[0] == \"\\r\" ? \"\\u240d\" : \"\\u2424\", \"cm-invalidchar\"));\n txt$1.setAttribute(\"cm-text\", m[0]);\n builder.col += 1;\n } else {\n txt$1 = builder.cm.options.specialCharPlaceholder(m[0]);\n txt$1.setAttribute(\"cm-text\", m[0]);\n if (ie && ie_version < 9) { content.appendChild(elt(\"span\", [txt$1])); }\n else { content.appendChild(txt$1); }\n builder.col += 1;\n }\n builder.map.push(builder.pos, builder.pos + 1, txt$1);\n builder.pos++;\n }\n }\n builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32;\n if (style || startStyle || endStyle || mustWrap || css) {\n var fullStyle = style || \"\";\n if (startStyle) { fullStyle += startStyle; }\n if (endStyle) { fullStyle += endStyle; }\n var token = elt(\"span\", [content], fullStyle, css);\n if (attributes) {\n for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != \"style\" && attr != \"class\")\n { token.setAttribute(attr, attributes[attr]); } }\n }\n return builder.content.appendChild(token)\n }\n builder.content.appendChild(content);\n }\n\n // Change some spaces to NBSP to prevent the browser from collapsing\n // trailing spaces at the end of a line when rendering text (issue #1362).\n function splitSpaces(text, trailingBefore) {\n if (text.length > 1 && !/ /.test(text)) { return text }\n var spaceBefore = trailingBefore, result = \"\";\n for (var i = 0; i < text.length; i++) {\n var ch = text.charAt(i);\n if (ch == \" \" && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))\n { ch = \"\\u00a0\"; }\n result += ch;\n spaceBefore = ch == \" \";\n }\n return result\n }\n\n // Work around nonsense dimensions being reported for stretches of\n // right-to-left text.\n function buildTokenBadBidi(inner, order) {\n return function (builder, text, style, startStyle, endStyle, css, attributes) {\n style = style ? style + \" cm-force-border\" : \"cm-force-border\";\n var start = builder.pos, end = start + text.length;\n for (;;) {\n // Find the part that overlaps with the start of this text\n var part = (void 0);\n for (var i = 0; i < order.length; i++) {\n part = order[i];\n if (part.to > start && part.from <= start) { break }\n }\n if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) }\n inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes);\n startStyle = null;\n text = text.slice(part.to - start);\n start = part.to;\n }\n }\n }\n\n function buildCollapsedSpan(builder, size, marker, ignoreWidget) {\n var widget = !ignoreWidget && marker.widgetNode;\n if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); }\n if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {\n if (!widget)\n { widget = builder.content.appendChild(document.createElement(\"span\")); }\n widget.setAttribute(\"cm-marker\", marker.id);\n }\n if (widget) {\n builder.cm.display.input.setUneditable(widget);\n builder.content.appendChild(widget);\n }\n builder.pos += size;\n builder.trailingSpace = false;\n }\n\n // Outputs a number of spans to make up a line, taking highlighting\n // and marked text into account.\n function insertLineContent(line, builder, styles) {\n var spans = line.markedSpans, allText = line.text, at = 0;\n if (!spans) {\n for (var i$1 = 1; i$1 < styles.length; i$1+=2)\n { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); }\n return\n }\n\n var len = allText.length, pos = 0, i = 1, text = \"\", style, css;\n var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes;\n for (;;) {\n if (nextChange == pos) { // Update current marker set\n spanStyle = spanEndStyle = spanStartStyle = css = \"\";\n attributes = null;\n collapsed = null; nextChange = Infinity;\n var foundBookmarks = [], endStyles = (void 0);\n for (var j = 0; j < spans.length; ++j) {\n var sp = spans[j], m = sp.marker;\n if (m.type == \"bookmark\" && sp.from == pos && m.widgetNode) {\n foundBookmarks.push(m);\n } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {\n if (sp.to != null && sp.to != pos && nextChange > sp.to) {\n nextChange = sp.to;\n spanEndStyle = \"\";\n }\n if (m.className) { spanStyle += \" \" + m.className; }\n if (m.css) { css = (css ? css + \";\" : \"\") + m.css; }\n if (m.startStyle && sp.from == pos) { spanStartStyle += \" \" + m.startStyle; }\n if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); }\n // support for the old title property\n // https://github.com/codemirror/CodeMirror/pull/5673\n if (m.title) { (attributes || (attributes = {})).title = m.title; }\n if (m.attributes) {\n for (var attr in m.attributes)\n { (attributes || (attributes = {}))[attr] = m.attributes[attr]; }\n }\n if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))\n { collapsed = sp; }\n } else if (sp.from > pos && nextChange > sp.from) {\n nextChange = sp.from;\n }\n }\n if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)\n { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += \" \" + endStyles[j$1]; } } }\n\n if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)\n { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } }\n if (collapsed && (collapsed.from || 0) == pos) {\n buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,\n collapsed.marker, collapsed.from == null);\n if (collapsed.to == null) { return }\n if (collapsed.to == pos) { collapsed = false; }\n }\n }\n if (pos >= len) { break }\n\n var upto = Math.min(len, nextChange);\n while (true) {\n if (text) {\n var end = pos + text.length;\n if (!collapsed) {\n var tokenText = end > upto ? text.slice(0, upto - pos) : text;\n builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,\n spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : \"\", css, attributes);\n }\n if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}\n pos = end;\n spanStartStyle = \"\";\n }\n text = allText.slice(at, at = styles[i++]);\n style = interpretTokenStyle(styles[i++], builder.cm.options);\n }\n }\n }\n\n\n // These objects are used to represent the visible (currently drawn)\n // part of the document. A LineView may correspond to multiple\n // logical lines, if those are connected by collapsed ranges.\n function LineView(doc, line, lineN) {\n // The starting line\n this.line = line;\n // Continuing lines, if any\n this.rest = visualLineContinued(line);\n // Number of logical lines in this visual line\n this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;\n this.node = this.text = null;\n this.hidden = lineIsHidden(doc, line);\n }\n\n // Create a range of LineView objects for the given lines.\n function buildViewArray(cm, from, to) {\n var array = [], nextPos;\n for (var pos = from; pos < to; pos = nextPos) {\n var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);\n nextPos = pos + view.size;\n array.push(view);\n }\n return array\n }\n\n var operationGroup = null;\n\n function pushOperation(op) {\n if (operationGroup) {\n operationGroup.ops.push(op);\n } else {\n op.ownsGroup = operationGroup = {\n ops: [op],\n delayedCallbacks: []\n };\n }\n }\n\n function fireCallbacksForOps(group) {\n // Calls delayed callbacks and cursorActivity handlers until no\n // new ones appear\n var callbacks = group.delayedCallbacks, i = 0;\n do {\n for (; i < callbacks.length; i++)\n { callbacks[i].call(null); }\n for (var j = 0; j < group.ops.length; j++) {\n var op = group.ops[j];\n if (op.cursorActivityHandlers)\n { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)\n { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } }\n }\n } while (i < callbacks.length)\n }\n\n function finishOperation(op, endCb) {\n var group = op.ownsGroup;\n if (!group) { return }\n\n try { fireCallbacksForOps(group); }\n finally {\n operationGroup = null;\n endCb(group);\n }\n }\n\n var orphanDelayedCallbacks = null;\n\n // Often, we want to signal events at a point where we are in the\n // middle of some work, but don't want the handler to start calling\n // other methods on the editor, which might be in an inconsistent\n // state or simply not expect any other events to happen.\n // signalLater looks whether there are any handlers, and schedules\n // them to be executed when the last operation ends, or, if no\n // operation is active, when a timeout fires.\n function signalLater(emitter, type /*, values...*/) {\n var arr = getHandlers(emitter, type);\n if (!arr.length) { return }\n var args = Array.prototype.slice.call(arguments, 2), list;\n if (operationGroup) {\n list = operationGroup.delayedCallbacks;\n } else if (orphanDelayedCallbacks) {\n list = orphanDelayedCallbacks;\n } else {\n list = orphanDelayedCallbacks = [];\n setTimeout(fireOrphanDelayed, 0);\n }\n var loop = function ( i ) {\n list.push(function () { return arr[i].apply(null, args); });\n };\n\n for (var i = 0; i < arr.length; ++i)\n loop( i );\n }\n\n function fireOrphanDelayed() {\n var delayed = orphanDelayedCallbacks;\n orphanDelayedCallbacks = null;\n for (var i = 0; i < delayed.length; ++i) { delayed[i](); }\n }\n\n // When an aspect of a line changes, a string is added to\n // lineView.changes. This updates the relevant part of the line's\n // DOM structure.\n function updateLineForChanges(cm, lineView, lineN, dims) {\n for (var j = 0; j < lineView.changes.length; j++) {\n var type = lineView.changes[j];\n if (type == \"text\") { updateLineText(cm, lineView); }\n else if (type == \"gutter\") { updateLineGutter(cm, lineView, lineN, dims); }\n else if (type == \"class\") { updateLineClasses(cm, lineView); }\n else if (type == \"widget\") { updateLineWidgets(cm, lineView, dims); }\n }\n lineView.changes = null;\n }\n\n // Lines with gutter elements, widgets or a background class need to\n // be wrapped, and have the extra elements added to the wrapper div\n function ensureLineWrapped(lineView) {\n if (lineView.node == lineView.text) {\n lineView.node = elt(\"div\", null, null, \"position: relative\");\n if (lineView.text.parentNode)\n { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); }\n lineView.node.appendChild(lineView.text);\n if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; }\n }\n return lineView.node\n }\n\n function updateLineBackground(cm, lineView) {\n var cls = lineView.bgClass ? lineView.bgClass + \" \" + (lineView.line.bgClass || \"\") : lineView.line.bgClass;\n if (cls) { cls += \" CodeMirror-linebackground\"; }\n if (lineView.background) {\n if (cls) { lineView.background.className = cls; }\n else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }\n } else if (cls) {\n var wrap = ensureLineWrapped(lineView);\n lineView.background = wrap.insertBefore(elt(\"div\", null, cls), wrap.firstChild);\n cm.display.input.setUneditable(lineView.background);\n }\n }\n\n // Wrapper around buildLineContent which will reuse the structure\n // in display.externalMeasured when possible.\n function getLineContent(cm, lineView) {\n var ext = cm.display.externalMeasured;\n if (ext && ext.line == lineView.line) {\n cm.display.externalMeasured = null;\n lineView.measure = ext.measure;\n return ext.built\n }\n return buildLineContent(cm, lineView)\n }\n\n // Redraw the line's text. Interacts with the background and text\n // classes because the mode may output tokens that influence these\n // classes.\n function updateLineText(cm, lineView) {\n var cls = lineView.text.className;\n var built = getLineContent(cm, lineView);\n if (lineView.text == lineView.node) { lineView.node = built.pre; }\n lineView.text.parentNode.replaceChild(built.pre, lineView.text);\n lineView.text = built.pre;\n if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {\n lineView.bgClass = built.bgClass;\n lineView.textClass = built.textClass;\n updateLineClasses(cm, lineView);\n } else if (cls) {\n lineView.text.className = cls;\n }\n }\n\n function updateLineClasses(cm, lineView) {\n updateLineBackground(cm, lineView);\n if (lineView.line.wrapClass)\n { ensureLineWrapped(lineView).className = lineView.line.wrapClass; }\n else if (lineView.node != lineView.text)\n { lineView.node.className = \"\"; }\n var textClass = lineView.textClass ? lineView.textClass + \" \" + (lineView.line.textClass || \"\") : lineView.line.textClass;\n lineView.text.className = textClass || \"\";\n }\n\n function updateLineGutter(cm, lineView, lineN, dims) {\n if (lineView.gutter) {\n lineView.node.removeChild(lineView.gutter);\n lineView.gutter = null;\n }\n if (lineView.gutterBackground) {\n lineView.node.removeChild(lineView.gutterBackground);\n lineView.gutterBackground = null;\n }\n if (lineView.line.gutterClass) {\n var wrap = ensureLineWrapped(lineView);\n lineView.gutterBackground = elt(\"div\", null, \"CodeMirror-gutter-background \" + lineView.line.gutterClass,\n (\"left: \" + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + \"px; width: \" + (dims.gutterTotalWidth) + \"px\"));\n cm.display.input.setUneditable(lineView.gutterBackground);\n wrap.insertBefore(lineView.gutterBackground, lineView.text);\n }\n var markers = lineView.line.gutterMarkers;\n if (cm.options.lineNumbers || markers) {\n var wrap$1 = ensureLineWrapped(lineView);\n var gutterWrap = lineView.gutter = elt(\"div\", null, \"CodeMirror-gutter-wrapper\", (\"left: \" + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + \"px\"));\n cm.display.input.setUneditable(gutterWrap);\n wrap$1.insertBefore(gutterWrap, lineView.text);\n if (lineView.line.gutterClass)\n { gutterWrap.className += \" \" + lineView.line.gutterClass; }\n if (cm.options.lineNumbers && (!markers || !markers[\"CodeMirror-linenumbers\"]))\n { lineView.lineNumber = gutterWrap.appendChild(\n elt(\"div\", lineNumberFor(cm.options, lineN),\n \"CodeMirror-linenumber CodeMirror-gutter-elt\",\n (\"left: \" + (dims.gutterLeft[\"CodeMirror-linenumbers\"]) + \"px; width: \" + (cm.display.lineNumInnerWidth) + \"px\"))); }\n if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) {\n var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id];\n if (found)\n { gutterWrap.appendChild(elt(\"div\", [found], \"CodeMirror-gutter-elt\",\n (\"left: \" + (dims.gutterLeft[id]) + \"px; width: \" + (dims.gutterWidth[id]) + \"px\"))); }\n } }\n }\n }\n\n function updateLineWidgets(cm, lineView, dims) {\n if (lineView.alignable) { lineView.alignable = null; }\n var isWidget = classTest(\"CodeMirror-linewidget\");\n for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {\n next = node.nextSibling;\n if (isWidget.test(node.className)) { lineView.node.removeChild(node); }\n }\n insertLineWidgets(cm, lineView, dims);\n }\n\n // Build a line's DOM representation from scratch\n function buildLineElement(cm, lineView, lineN, dims) {\n var built = getLineContent(cm, lineView);\n lineView.text = lineView.node = built.pre;\n if (built.bgClass) { lineView.bgClass = built.bgClass; }\n if (built.textClass) { lineView.textClass = built.textClass; }\n\n updateLineClasses(cm, lineView);\n updateLineGutter(cm, lineView, lineN, dims);\n insertLineWidgets(cm, lineView, dims);\n return lineView.node\n }\n\n // A lineView may contain multiple logical lines (when merged by\n // collapsed spans). The widgets for all of them need to be drawn.\n function insertLineWidgets(cm, lineView, dims) {\n insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);\n if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)\n { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } }\n }\n\n function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {\n if (!line.widgets) { return }\n var wrap = ensureLineWrapped(lineView);\n for (var i = 0, ws = line.widgets; i < ws.length; ++i) {\n var widget = ws[i], node = elt(\"div\", [widget.node], \"CodeMirror-linewidget\" + (widget.className ? \" \" + widget.className : \"\"));\n if (!widget.handleMouseEvents) { node.setAttribute(\"cm-ignore-events\", \"true\"); }\n positionLineWidget(widget, node, lineView, dims);\n cm.display.input.setUneditable(node);\n if (allowAbove && widget.above)\n { wrap.insertBefore(node, lineView.gutter || lineView.text); }\n else\n { wrap.appendChild(node); }\n signalLater(widget, \"redraw\");\n }\n }\n\n function positionLineWidget(widget, node, lineView, dims) {\n if (widget.noHScroll) {\n (lineView.alignable || (lineView.alignable = [])).push(node);\n var width = dims.wrapperWidth;\n node.style.left = dims.fixedPos + \"px\";\n if (!widget.coverGutter) {\n width -= dims.gutterTotalWidth;\n node.style.paddingLeft = dims.gutterTotalWidth + \"px\";\n }\n node.style.width = width + \"px\";\n }\n if (widget.coverGutter) {\n node.style.zIndex = 5;\n node.style.position = \"relative\";\n if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + \"px\"; }\n }\n }\n\n function widgetHeight(widget) {\n if (widget.height != null) { return widget.height }\n var cm = widget.doc.cm;\n if (!cm) { return 0 }\n if (!contains(document.body, widget.node)) {\n var parentStyle = \"position: relative;\";\n if (widget.coverGutter)\n { parentStyle += \"margin-left: -\" + cm.display.gutters.offsetWidth + \"px;\"; }\n if (widget.noHScroll)\n { parentStyle += \"width: \" + cm.display.wrapper.clientWidth + \"px;\"; }\n removeChildrenAndAdd(cm.display.measure, elt(\"div\", [widget.node], null, parentStyle));\n }\n return widget.height = widget.node.parentNode.offsetHeight\n }\n\n // Return true when the given mouse event happened in a widget\n function eventInWidget(display, e) {\n for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {\n if (!n || (n.nodeType == 1 && n.getAttribute(\"cm-ignore-events\") == \"true\") ||\n (n.parentNode == display.sizer && n != display.mover))\n { return true }\n }\n }\n\n // POSITION MEASUREMENT\n\n function paddingTop(display) {return display.lineSpace.offsetTop}\n function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}\n function paddingH(display) {\n if (display.cachedPaddingH) { return display.cachedPaddingH }\n var e = removeChildrenAndAdd(display.measure, elt(\"pre\", \"x\", \"CodeMirror-line-like\"));\n var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;\n var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};\n if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; }\n return data\n }\n\n function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }\n function displayWidth(cm) {\n return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth\n }\n function displayHeight(cm) {\n return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight\n }\n\n // Ensure the lineView.wrapping.heights array is populated. This is\n // an array of bottom offsets for the lines that make up a drawn\n // line. When lineWrapping is on, there might be more than one\n // height.\n function ensureLineHeights(cm, lineView, rect) {\n var wrapping = cm.options.lineWrapping;\n var curWidth = wrapping && displayWidth(cm);\n if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {\n var heights = lineView.measure.heights = [];\n if (wrapping) {\n lineView.measure.width = curWidth;\n var rects = lineView.text.firstChild.getClientRects();\n for (var i = 0; i < rects.length - 1; i++) {\n var cur = rects[i], next = rects[i + 1];\n if (Math.abs(cur.bottom - next.bottom) > 2)\n { heights.push((cur.bottom + next.top) / 2 - rect.top); }\n }\n }\n heights.push(rect.bottom - rect.top);\n }\n }\n\n // Find a line map (mapping character offsets to text nodes) and a\n // measurement cache for the given line number. (A line view might\n // contain multiple lines when collapsed ranges are present.)\n function mapFromLineView(lineView, line, lineN) {\n if (lineView.line == line)\n { return {map: lineView.measure.map, cache: lineView.measure.cache} }\n for (var i = 0; i < lineView.rest.length; i++)\n { if (lineView.rest[i] == line)\n { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }\n for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)\n { if (lineNo(lineView.rest[i$1]) > lineN)\n { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }\n }\n\n // Render a line into the hidden node display.externalMeasured. Used\n // when measurement is needed for a line that's not in the viewport.\n function updateExternalMeasurement(cm, line) {\n line = visualLine(line);\n var lineN = lineNo(line);\n var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);\n view.lineN = lineN;\n var built = view.built = buildLineContent(cm, view);\n view.text = built.pre;\n removeChildrenAndAdd(cm.display.lineMeasure, built.pre);\n return view\n }\n\n // Get a {top, bottom, left, right} box (in line-local coordinates)\n // for a given character.\n function measureChar(cm, line, ch, bias) {\n return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)\n }\n\n // Find a line view that corresponds to the given line number.\n function findViewForLine(cm, lineN) {\n if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)\n { return cm.display.view[findViewIndex(cm, lineN)] }\n var ext = cm.display.externalMeasured;\n if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)\n { return ext }\n }\n\n // Measurement can be split in two steps, the set-up work that\n // applies to the whole line, and the measurement of the actual\n // character. Functions like coordsChar, that need to do a lot of\n // measurements in a row, can thus ensure that the set-up work is\n // only done once.\n function prepareMeasureForLine(cm, line) {\n var lineN = lineNo(line);\n var view = findViewForLine(cm, lineN);\n if (view && !view.text) {\n view = null;\n } else if (view && view.changes) {\n updateLineForChanges(cm, view, lineN, getDimensions(cm));\n cm.curOp.forceUpdate = true;\n }\n if (!view)\n { view = updateExternalMeasurement(cm, line); }\n\n var info = mapFromLineView(view, line, lineN);\n return {\n line: line, view: view, rect: null,\n map: info.map, cache: info.cache, before: info.before,\n hasHeights: false\n }\n }\n\n // Given a prepared measurement object, measures the position of an\n // actual character (or fetches it from the cache).\n function measureCharPrepared(cm, prepared, ch, bias, varHeight) {\n if (prepared.before) { ch = -1; }\n var key = ch + (bias || \"\"), found;\n if (prepared.cache.hasOwnProperty(key)) {\n found = prepared.cache[key];\n } else {\n if (!prepared.rect)\n { prepared.rect = prepared.view.text.getBoundingClientRect(); }\n if (!prepared.hasHeights) {\n ensureLineHeights(cm, prepared.view, prepared.rect);\n prepared.hasHeights = true;\n }\n found = measureCharInner(cm, prepared, ch, bias);\n if (!found.bogus) { prepared.cache[key] = found; }\n }\n return {left: found.left, right: found.right,\n top: varHeight ? found.rtop : found.top,\n bottom: varHeight ? found.rbottom : found.bottom}\n }\n\n var nullRect = {left: 0, right: 0, top: 0, bottom: 0};\n\n function nodeAndOffsetInLineMap(map, ch, bias) {\n var node, start, end, collapse, mStart, mEnd;\n // First, search the line map for the text node corresponding to,\n // or closest to, the target character.\n for (var i = 0; i < map.length; i += 3) {\n mStart = map[i];\n mEnd = map[i + 1];\n if (ch < mStart) {\n start = 0; end = 1;\n collapse = \"left\";\n } else if (ch < mEnd) {\n start = ch - mStart;\n end = start + 1;\n } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {\n end = mEnd - mStart;\n start = end - 1;\n if (ch >= mEnd) { collapse = \"right\"; }\n }\n if (start != null) {\n node = map[i + 2];\n if (mStart == mEnd && bias == (node.insertLeft ? \"left\" : \"right\"))\n { collapse = bias; }\n if (bias == \"left\" && start == 0)\n { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {\n node = map[(i -= 3) + 2];\n collapse = \"left\";\n } }\n if (bias == \"right\" && start == mEnd - mStart)\n { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {\n node = map[(i += 3) + 2];\n collapse = \"right\";\n } }\n break\n }\n }\n return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}\n }\n\n function getUsefulRect(rects, bias) {\n var rect = nullRect;\n if (bias == \"left\") { for (var i = 0; i < rects.length; i++) {\n if ((rect = rects[i]).left != rect.right) { break }\n } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {\n if ((rect = rects[i$1]).left != rect.right) { break }\n } }\n return rect\n }\n\n function measureCharInner(cm, prepared, ch, bias) {\n var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);\n var node = place.node, start = place.start, end = place.end, collapse = place.collapse;\n\n var rect;\n if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.\n for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned\n while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; }\n while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; }\n if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)\n { rect = node.parentNode.getBoundingClientRect(); }\n else\n { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); }\n if (rect.left || rect.right || start == 0) { break }\n end = start;\n start = start - 1;\n collapse = \"right\";\n }\n if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); }\n } else { // If it is a widget, simply get the box for the whole widget.\n if (start > 0) { collapse = bias = \"right\"; }\n var rects;\n if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)\n { rect = rects[bias == \"right\" ? rects.length - 1 : 0]; }\n else\n { rect = node.getBoundingClientRect(); }\n }\n if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {\n var rSpan = node.parentNode.getClientRects()[0];\n if (rSpan)\n { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; }\n else\n { rect = nullRect; }\n }\n\n var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;\n var mid = (rtop + rbot) / 2;\n var heights = prepared.view.measure.heights;\n var i = 0;\n for (; i < heights.length - 1; i++)\n { if (mid < heights[i]) { break } }\n var top = i ? heights[i - 1] : 0, bot = heights[i];\n var result = {left: (collapse == \"right\" ? rect.right : rect.left) - prepared.rect.left,\n right: (collapse == \"left\" ? rect.left : rect.right) - prepared.rect.left,\n top: top, bottom: bot};\n if (!rect.left && !rect.right) { result.bogus = true; }\n if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }\n\n return result\n }\n\n // Work around problem with bounding client rects on ranges being\n // returned incorrectly when zoomed on IE10 and below.\n function maybeUpdateRectForZooming(measure, rect) {\n if (!window.screen || screen.logicalXDPI == null ||\n screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))\n { return rect }\n var scaleX = screen.logicalXDPI / screen.deviceXDPI;\n var scaleY = screen.logicalYDPI / screen.deviceYDPI;\n return {left: rect.left * scaleX, right: rect.right * scaleX,\n top: rect.top * scaleY, bottom: rect.bottom * scaleY}\n }\n\n function clearLineMeasurementCacheFor(lineView) {\n if (lineView.measure) {\n lineView.measure.cache = {};\n lineView.measure.heights = null;\n if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)\n { lineView.measure.caches[i] = {}; } }\n }\n }\n\n function clearLineMeasurementCache(cm) {\n cm.display.externalMeasure = null;\n removeChildren(cm.display.lineMeasure);\n for (var i = 0; i < cm.display.view.length; i++)\n { clearLineMeasurementCacheFor(cm.display.view[i]); }\n }\n\n function clearCaches(cm) {\n clearLineMeasurementCache(cm);\n cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;\n if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; }\n cm.display.lineNumChars = null;\n }\n\n function pageScrollX() {\n // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206\n // which causes page_Offset and bounding client rects to use\n // different reference viewports and invalidate our calculations.\n if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }\n return window.pageXOffset || (document.documentElement || document.body).scrollLeft\n }\n function pageScrollY() {\n if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }\n return window.pageYOffset || (document.documentElement || document.body).scrollTop\n }\n\n function widgetTopHeight(lineObj) {\n var height = 0;\n if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)\n { height += widgetHeight(lineObj.widgets[i]); } } }\n return height\n }\n\n // Converts a {top, bottom, left, right} box from line-local\n // coordinates into another coordinate system. Context may be one of\n // \"line\", \"div\" (display.lineDiv), \"local\"./null (editor), \"window\",\n // or \"page\".\n function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {\n if (!includeWidgets) {\n var height = widgetTopHeight(lineObj);\n rect.top += height; rect.bottom += height;\n }\n if (context == \"line\") { return rect }\n if (!context) { context = \"local\"; }\n var yOff = heightAtLine(lineObj);\n if (context == \"local\") { yOff += paddingTop(cm.display); }\n else { yOff -= cm.display.viewOffset; }\n if (context == \"page\" || context == \"window\") {\n var lOff = cm.display.lineSpace.getBoundingClientRect();\n yOff += lOff.top + (context == \"window\" ? 0 : pageScrollY());\n var xOff = lOff.left + (context == \"window\" ? 0 : pageScrollX());\n rect.left += xOff; rect.right += xOff;\n }\n rect.top += yOff; rect.bottom += yOff;\n return rect\n }\n\n // Coverts a box from \"div\" coords to another coordinate system.\n // Context may be \"window\", \"page\", \"div\", or \"local\"./null.\n function fromCoordSystem(cm, coords, context) {\n if (context == \"div\") { return coords }\n var left = coords.left, top = coords.top;\n // First move into \"page\" coordinate system\n if (context == \"page\") {\n left -= pageScrollX();\n top -= pageScrollY();\n } else if (context == \"local\" || !context) {\n var localBox = cm.display.sizer.getBoundingClientRect();\n left += localBox.left;\n top += localBox.top;\n }\n\n var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();\n return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}\n }\n\n function charCoords(cm, pos, context, lineObj, bias) {\n if (!lineObj) { lineObj = getLine(cm.doc, pos.line); }\n return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)\n }\n\n // Returns a box for a given cursor position, which may have an\n // 'other' property containing the position of the secondary cursor\n // on a bidi boundary.\n // A cursor Pos(line, char, \"before\") is on the same visual line as `char - 1`\n // and after `char - 1` in writing order of `char - 1`\n // A cursor Pos(line, char, \"after\") is on the same visual line as `char`\n // and before `char` in writing order of `char`\n // Examples (upper-case letters are RTL, lower-case are LTR):\n // Pos(0, 1, ...)\n // before after\n // ab a|b a|b\n // aB a|B aB|\n // Ab |Ab A|b\n // AB B|A B|A\n // Every position after the last character on a line is considered to stick\n // to the last character on the line.\n function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {\n lineObj = lineObj || getLine(cm.doc, pos.line);\n if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }\n function get(ch, right) {\n var m = measureCharPrepared(cm, preparedMeasure, ch, right ? \"right\" : \"left\", varHeight);\n if (right) { m.left = m.right; } else { m.right = m.left; }\n return intoCoordSystem(cm, lineObj, m, context)\n }\n var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky;\n if (ch >= lineObj.text.length) {\n ch = lineObj.text.length;\n sticky = \"before\";\n } else if (ch <= 0) {\n ch = 0;\n sticky = \"after\";\n }\n if (!order) { return get(sticky == \"before\" ? ch - 1 : ch, sticky == \"before\") }\n\n function getBidi(ch, partPos, invert) {\n var part = order[partPos], right = part.level == 1;\n return get(invert ? ch - 1 : ch, right != invert)\n }\n var partPos = getBidiPartAt(order, ch, sticky);\n var other = bidiOther;\n var val = getBidi(ch, partPos, sticky == \"before\");\n if (other != null) { val.other = getBidi(ch, other, sticky != \"before\"); }\n return val\n }\n\n // Used to cheaply estimate the coordinates for a position. Used for\n // intermediate scroll updates.\n function estimateCoords(cm, pos) {\n var left = 0;\n pos = clipPos(cm.doc, pos);\n if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; }\n var lineObj = getLine(cm.doc, pos.line);\n var top = heightAtLine(lineObj) + paddingTop(cm.display);\n return {left: left, right: left, top: top, bottom: top + lineObj.height}\n }\n\n // Positions returned by coordsChar contain some extra information.\n // xRel is the relative x position of the input coordinates compared\n // to the found position (so xRel > 0 means the coordinates are to\n // the right of the character position, for example). When outside\n // is true, that means the coordinates lie outside the line's\n // vertical range.\n function PosWithInfo(line, ch, sticky, outside, xRel) {\n var pos = Pos(line, ch, sticky);\n pos.xRel = xRel;\n if (outside) { pos.outside = outside; }\n return pos\n }\n\n // Compute the character position closest to the given coordinates.\n // Input must be lineSpace-local (\"div\" coordinate system).\n function coordsChar(cm, x, y) {\n var doc = cm.doc;\n y += cm.display.viewOffset;\n if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) }\n var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;\n if (lineN > last)\n { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) }\n if (x < 0) { x = 0; }\n\n var lineObj = getLine(doc, lineN);\n for (;;) {\n var found = coordsCharInner(cm, lineObj, lineN, x, y);\n var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0));\n if (!collapsed) { return found }\n var rangeEnd = collapsed.find(1);\n if (rangeEnd.line == lineN) { return rangeEnd }\n lineObj = getLine(doc, lineN = rangeEnd.line);\n }\n }\n\n function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {\n y -= widgetTopHeight(lineObj);\n var end = lineObj.text.length;\n var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0);\n end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end);\n return {begin: begin, end: end}\n }\n\n function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {\n if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }\n var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), \"line\").top;\n return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)\n }\n\n // Returns true if the given side of a box is after the given\n // coordinates, in top-to-bottom, left-to-right order.\n function boxIsAfter(box, x, y, left) {\n return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x\n }\n\n function coordsCharInner(cm, lineObj, lineNo, x, y) {\n // Move y into line-local coordinate space\n y -= heightAtLine(lineObj);\n var preparedMeasure = prepareMeasureForLine(cm, lineObj);\n // When directly calling `measureCharPrepared`, we have to adjust\n // for the widgets at this line.\n var widgetHeight = widgetTopHeight(lineObj);\n var begin = 0, end = lineObj.text.length, ltr = true;\n\n var order = getOrder(lineObj, cm.doc.direction);\n // If the line isn't plain left-to-right text, first figure out\n // which bidi section the coordinates fall into.\n if (order) {\n var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)\n (cm, lineObj, lineNo, preparedMeasure, order, x, y);\n ltr = part.level != 1;\n // The awkward -1 offsets are needed because findFirst (called\n // on these below) will treat its first bound as inclusive,\n // second as exclusive, but we want to actually address the\n // characters in the part's range\n begin = ltr ? part.from : part.to - 1;\n end = ltr ? part.to : part.from - 1;\n }\n\n // A binary search to find the first character whose bounding box\n // starts after the coordinates. If we run across any whose box wrap\n // the coordinates, store that.\n var chAround = null, boxAround = null;\n var ch = findFirst(function (ch) {\n var box = measureCharPrepared(cm, preparedMeasure, ch);\n box.top += widgetHeight; box.bottom += widgetHeight;\n if (!boxIsAfter(box, x, y, false)) { return false }\n if (box.top <= y && box.left <= x) {\n chAround = ch;\n boxAround = box;\n }\n return true\n }, begin, end);\n\n var baseX, sticky, outside = false;\n // If a box around the coordinates was found, use that\n if (boxAround) {\n // Distinguish coordinates nearer to the left or right side of the box\n var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr;\n ch = chAround + (atStart ? 0 : 1);\n sticky = atStart ? \"after\" : \"before\";\n baseX = atLeft ? boxAround.left : boxAround.right;\n } else {\n // (Adjust for extended bound, if necessary.)\n if (!ltr && (ch == end || ch == begin)) { ch++; }\n // To determine which side to associate with, get the box to the\n // left of the character and compare it's vertical position to the\n // coordinates\n sticky = ch == 0 ? \"after\" : ch == lineObj.text.length ? \"before\" :\n (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ?\n \"after\" : \"before\";\n // Now get accurate coordinates for this place, in order to get a\n // base X position\n var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), \"line\", lineObj, preparedMeasure);\n baseX = coords.left;\n outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0;\n }\n\n ch = skipExtendingChars(lineObj.text, ch, 1);\n return PosWithInfo(lineNo, ch, sticky, outside, x - baseX)\n }\n\n function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) {\n // Bidi parts are sorted left-to-right, and in a non-line-wrapping\n // situation, we can take this ordering to correspond to the visual\n // ordering. This finds the first part whose end is after the given\n // coordinates.\n var index = findFirst(function (i) {\n var part = order[i], ltr = part.level != 1;\n return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? \"before\" : \"after\"),\n \"line\", lineObj, preparedMeasure), x, y, true)\n }, 0, order.length - 1);\n var part = order[index];\n // If this isn't the first part, the part's start is also after\n // the coordinates, and the coordinates aren't on the same line as\n // that start, move one part back.\n if (index > 0) {\n var ltr = part.level != 1;\n var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? \"after\" : \"before\"),\n \"line\", lineObj, preparedMeasure);\n if (boxIsAfter(start, x, y, true) && start.top > y)\n { part = order[index - 1]; }\n }\n return part\n }\n\n function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {\n // In a wrapped line, rtl text on wrapping boundaries can do things\n // that don't correspond to the ordering in our `order` array at\n // all, so a binary search doesn't work, and we want to return a\n // part that only spans one line so that the binary search in\n // coordsCharInner is safe. As such, we first find the extent of the\n // wrapped line, and then do a flat search in which we discard any\n // spans that aren't on the line.\n var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);\n var begin = ref.begin;\n var end = ref.end;\n if (/\\s/.test(lineObj.text.charAt(end - 1))) { end--; }\n var part = null, closestDist = null;\n for (var i = 0; i < order.length; i++) {\n var p = order[i];\n if (p.from >= end || p.to <= begin) { continue }\n var ltr = p.level != 1;\n var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right;\n // Weigh against spans ending before this, so that they are only\n // picked if nothing ends after\n var dist = endX < x ? x - endX + 1e9 : endX - x;\n if (!part || closestDist > dist) {\n part = p;\n closestDist = dist;\n }\n }\n if (!part) { part = order[order.length - 1]; }\n // Clip the part to the wrapped line.\n if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; }\n if (part.to > end) { part = {from: part.from, to: end, level: part.level}; }\n return part\n }\n\n var measureText;\n // Compute the default text height.\n function textHeight(display) {\n if (display.cachedTextHeight != null) { return display.cachedTextHeight }\n if (measureText == null) {\n measureText = elt(\"pre\", null, \"CodeMirror-line-like\");\n // Measure a bunch of lines, for browsers that compute\n // fractional heights.\n for (var i = 0; i < 49; ++i) {\n measureText.appendChild(document.createTextNode(\"x\"));\n measureText.appendChild(elt(\"br\"));\n }\n measureText.appendChild(document.createTextNode(\"x\"));\n }\n removeChildrenAndAdd(display.measure, measureText);\n var height = measureText.offsetHeight / 50;\n if (height > 3) { display.cachedTextHeight = height; }\n removeChildren(display.measure);\n return height || 1\n }\n\n // Compute the default character width.\n function charWidth(display) {\n if (display.cachedCharWidth != null) { return display.cachedCharWidth }\n var anchor = elt(\"span\", \"xxxxxxxxxx\");\n var pre = elt(\"pre\", [anchor], \"CodeMirror-line-like\");\n removeChildrenAndAdd(display.measure, pre);\n var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;\n if (width > 2) { display.cachedCharWidth = width; }\n return width || 10\n }\n\n // Do a bulk-read of the DOM positions and sizes needed to draw the\n // view, so that we don't interleave reading and writing to the DOM.\n function getDimensions(cm) {\n var d = cm.display, left = {}, width = {};\n var gutterLeft = d.gutters.clientLeft;\n for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {\n var id = cm.display.gutterSpecs[i].className;\n left[id] = n.offsetLeft + n.clientLeft + gutterLeft;\n width[id] = n.clientWidth;\n }\n return {fixedPos: compensateForHScroll(d),\n gutterTotalWidth: d.gutters.offsetWidth,\n gutterLeft: left,\n gutterWidth: width,\n wrapperWidth: d.wrapper.clientWidth}\n }\n\n // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,\n // but using getBoundingClientRect to get a sub-pixel-accurate\n // result.\n function compensateForHScroll(display) {\n return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left\n }\n\n // Returns a function that estimates the height of a line, to use as\n // first approximation until the line becomes visible (and is thus\n // properly measurable).\n function estimateHeight(cm) {\n var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;\n var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);\n return function (line) {\n if (lineIsHidden(cm.doc, line)) { return 0 }\n\n var widgetsHeight = 0;\n if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {\n if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; }\n } }\n\n if (wrapping)\n { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }\n else\n { return widgetsHeight + th }\n }\n }\n\n function estimateLineHeights(cm) {\n var doc = cm.doc, est = estimateHeight(cm);\n doc.iter(function (line) {\n var estHeight = est(line);\n if (estHeight != line.height) { updateLineHeight(line, estHeight); }\n });\n }\n\n // Given a mouse event, find the corresponding position. If liberal\n // is false, it checks whether a gutter or scrollbar was clicked,\n // and returns null if it was. forRect is used by rectangular\n // selections, and tries to estimate a character position even for\n // coordinates beyond the right of the text.\n function posFromMouse(cm, e, liberal, forRect) {\n var display = cm.display;\n if (!liberal && e_target(e).getAttribute(\"cm-not-content\") == \"true\") { return null }\n\n var x, y, space = display.lineSpace.getBoundingClientRect();\n // Fails unpredictably on IE[67] when mouse is dragged around quickly.\n try { x = e.clientX - space.left; y = e.clientY - space.top; }\n catch (e) { return null }\n var coords = coordsChar(cm, x, y), line;\n if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {\n var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;\n coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));\n }\n return coords\n }\n\n // Find the view element corresponding to a given line. Return null\n // when the line isn't visible.\n function findViewIndex(cm, n) {\n if (n >= cm.display.viewTo) { return null }\n n -= cm.display.viewFrom;\n if (n < 0) { return null }\n var view = cm.display.view;\n for (var i = 0; i < view.length; i++) {\n n -= view[i].size;\n if (n < 0) { return i }\n }\n }\n\n // Updates the display.view data structure for a given change to the\n // document. From and to are in pre-change coordinates. Lendiff is\n // the amount of lines added or subtracted by the change. This is\n // used for changes that span multiple lines, or change the way\n // lines are divided into visual lines. regLineChange (below)\n // registers single-line changes.\n function regChange(cm, from, to, lendiff) {\n if (from == null) { from = cm.doc.first; }\n if (to == null) { to = cm.doc.first + cm.doc.size; }\n if (!lendiff) { lendiff = 0; }\n\n var display = cm.display;\n if (lendiff && to < display.viewTo &&\n (display.updateLineNumbers == null || display.updateLineNumbers > from))\n { display.updateLineNumbers = from; }\n\n cm.curOp.viewChanged = true;\n\n if (from >= display.viewTo) { // Change after\n if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)\n { resetView(cm); }\n } else if (to <= display.viewFrom) { // Change before\n if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {\n resetView(cm);\n } else {\n display.viewFrom += lendiff;\n display.viewTo += lendiff;\n }\n } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap\n resetView(cm);\n } else if (from <= display.viewFrom) { // Top overlap\n var cut = viewCuttingPoint(cm, to, to + lendiff, 1);\n if (cut) {\n display.view = display.view.slice(cut.index);\n display.viewFrom = cut.lineN;\n display.viewTo += lendiff;\n } else {\n resetView(cm);\n }\n } else if (to >= display.viewTo) { // Bottom overlap\n var cut$1 = viewCuttingPoint(cm, from, from, -1);\n if (cut$1) {\n display.view = display.view.slice(0, cut$1.index);\n display.viewTo = cut$1.lineN;\n } else {\n resetView(cm);\n }\n } else { // Gap in the middle\n var cutTop = viewCuttingPoint(cm, from, from, -1);\n var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);\n if (cutTop && cutBot) {\n display.view = display.view.slice(0, cutTop.index)\n .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))\n .concat(display.view.slice(cutBot.index));\n display.viewTo += lendiff;\n } else {\n resetView(cm);\n }\n }\n\n var ext = display.externalMeasured;\n if (ext) {\n if (to < ext.lineN)\n { ext.lineN += lendiff; }\n else if (from < ext.lineN + ext.size)\n { display.externalMeasured = null; }\n }\n }\n\n // Register a change to a single line. Type must be one of \"text\",\n // \"gutter\", \"class\", \"widget\"\n function regLineChange(cm, line, type) {\n cm.curOp.viewChanged = true;\n var display = cm.display, ext = cm.display.externalMeasured;\n if (ext && line >= ext.lineN && line < ext.lineN + ext.size)\n { display.externalMeasured = null; }\n\n if (line < display.viewFrom || line >= display.viewTo) { return }\n var lineView = display.view[findViewIndex(cm, line)];\n if (lineView.node == null) { return }\n var arr = lineView.changes || (lineView.changes = []);\n if (indexOf(arr, type) == -1) { arr.push(type); }\n }\n\n // Clear the view.\n function resetView(cm) {\n cm.display.viewFrom = cm.display.viewTo = cm.doc.first;\n cm.display.view = [];\n cm.display.viewOffset = 0;\n }\n\n function viewCuttingPoint(cm, oldN, newN, dir) {\n var index = findViewIndex(cm, oldN), diff, view = cm.display.view;\n if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)\n { return {index: index, lineN: newN} }\n var n = cm.display.viewFrom;\n for (var i = 0; i < index; i++)\n { n += view[i].size; }\n if (n != oldN) {\n if (dir > 0) {\n if (index == view.length - 1) { return null }\n diff = (n + view[index].size) - oldN;\n index++;\n } else {\n diff = n - oldN;\n }\n oldN += diff; newN += diff;\n }\n while (visualLineNo(cm.doc, newN) != newN) {\n if (index == (dir < 0 ? 0 : view.length - 1)) { return null }\n newN += dir * view[index - (dir < 0 ? 1 : 0)].size;\n index += dir;\n }\n return {index: index, lineN: newN}\n }\n\n // Force the view to cover a given range, adding empty view element\n // or clipping off existing ones as needed.\n function adjustView(cm, from, to) {\n var display = cm.display, view = display.view;\n if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {\n display.view = buildViewArray(cm, from, to);\n display.viewFrom = from;\n } else {\n if (display.viewFrom > from)\n { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); }\n else if (display.viewFrom < from)\n { display.view = display.view.slice(findViewIndex(cm, from)); }\n display.viewFrom = from;\n if (display.viewTo < to)\n { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); }\n else if (display.viewTo > to)\n { display.view = display.view.slice(0, findViewIndex(cm, to)); }\n }\n display.viewTo = to;\n }\n\n // Count the number of lines in the view whose DOM representation is\n // out of date (or nonexistent).\n function countDirtyView(cm) {\n var view = cm.display.view, dirty = 0;\n for (var i = 0; i < view.length; i++) {\n var lineView = view[i];\n if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; }\n }\n return dirty\n }\n\n function updateSelection(cm) {\n cm.display.input.showSelection(cm.display.input.prepareSelection());\n }\n\n function prepareSelection(cm, primary) {\n if ( primary === void 0 ) primary = true;\n\n var doc = cm.doc, result = {};\n var curFragment = result.cursors = document.createDocumentFragment();\n var selFragment = result.selection = document.createDocumentFragment();\n\n for (var i = 0; i < doc.sel.ranges.length; i++) {\n if (!primary && i == doc.sel.primIndex) { continue }\n var range = doc.sel.ranges[i];\n if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue }\n var collapsed = range.empty();\n if (collapsed || cm.options.showCursorWhenSelecting)\n { drawSelectionCursor(cm, range.head, curFragment); }\n if (!collapsed)\n { drawSelectionRange(cm, range, selFragment); }\n }\n return result\n }\n\n // Draws a cursor for the given range\n function drawSelectionCursor(cm, head, output) {\n var pos = cursorCoords(cm, head, \"div\", null, null, !cm.options.singleCursorHeightPerLine);\n\n var cursor = output.appendChild(elt(\"div\", \"\\u00a0\", \"CodeMirror-cursor\"));\n cursor.style.left = pos.left + \"px\";\n cursor.style.top = pos.top + \"px\";\n cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + \"px\";\n\n if (pos.other) {\n // Secondary cursor, shown when on a 'jump' in bi-directional text\n var otherCursor = output.appendChild(elt(\"div\", \"\\u00a0\", \"CodeMirror-cursor CodeMirror-secondarycursor\"));\n otherCursor.style.display = \"\";\n otherCursor.style.left = pos.other.left + \"px\";\n otherCursor.style.top = pos.other.top + \"px\";\n otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + \"px\";\n }\n }\n\n function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }\n\n // Draws the given range as a highlighted selection\n function drawSelectionRange(cm, range, output) {\n var display = cm.display, doc = cm.doc;\n var fragment = document.createDocumentFragment();\n var padding = paddingH(cm.display), leftSide = padding.left;\n var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;\n var docLTR = doc.direction == \"ltr\";\n\n function add(left, top, width, bottom) {\n if (top < 0) { top = 0; }\n top = Math.round(top);\n bottom = Math.round(bottom);\n fragment.appendChild(elt(\"div\", null, \"CodeMirror-selected\", (\"position: absolute; left: \" + left + \"px;\\n top: \" + top + \"px; width: \" + (width == null ? rightSide - left : width) + \"px;\\n height: \" + (bottom - top) + \"px\")));\n }\n\n function drawForLine(line, fromArg, toArg) {\n var lineObj = getLine(doc, line);\n var lineLen = lineObj.text.length;\n var start, end;\n function coords(ch, bias) {\n return charCoords(cm, Pos(line, ch), \"div\", lineObj, bias)\n }\n\n function wrapX(pos, dir, side) {\n var extent = wrappedLineExtentChar(cm, lineObj, null, pos);\n var prop = (dir == \"ltr\") == (side == \"after\") ? \"left\" : \"right\";\n var ch = side == \"after\" ? extent.begin : extent.end - (/\\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1);\n return coords(ch, prop)[prop]\n }\n\n var order = getOrder(lineObj, doc.direction);\n iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {\n var ltr = dir == \"ltr\";\n var fromPos = coords(from, ltr ? \"left\" : \"right\");\n var toPos = coords(to - 1, ltr ? \"right\" : \"left\");\n\n var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen;\n var first = i == 0, last = !order || i == order.length - 1;\n if (toPos.top - fromPos.top <= 3) { // Single line\n var openLeft = (docLTR ? openStart : openEnd) && first;\n var openRight = (docLTR ? openEnd : openStart) && last;\n var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left;\n var right = openRight ? rightSide : (ltr ? toPos : fromPos).right;\n add(left, fromPos.top, right - left, fromPos.bottom);\n } else { // Multiple lines\n var topLeft, topRight, botLeft, botRight;\n if (ltr) {\n topLeft = docLTR && openStart && first ? leftSide : fromPos.left;\n topRight = docLTR ? rightSide : wrapX(from, dir, \"before\");\n botLeft = docLTR ? leftSide : wrapX(to, dir, \"after\");\n botRight = docLTR && openEnd && last ? rightSide : toPos.right;\n } else {\n topLeft = !docLTR ? leftSide : wrapX(from, dir, \"before\");\n topRight = !docLTR && openStart && first ? rightSide : fromPos.right;\n botLeft = !docLTR && openEnd && last ? leftSide : toPos.left;\n botRight = !docLTR ? rightSide : wrapX(to, dir, \"after\");\n }\n add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom);\n if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); }\n add(botLeft, toPos.top, botRight - botLeft, toPos.bottom);\n }\n\n if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; }\n if (cmpCoords(toPos, start) < 0) { start = toPos; }\n if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; }\n if (cmpCoords(toPos, end) < 0) { end = toPos; }\n });\n return {start: start, end: end}\n }\n\n var sFrom = range.from(), sTo = range.to();\n if (sFrom.line == sTo.line) {\n drawForLine(sFrom.line, sFrom.ch, sTo.ch);\n } else {\n var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);\n var singleVLine = visualLine(fromLine) == visualLine(toLine);\n var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;\n var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;\n if (singleVLine) {\n if (leftEnd.top < rightStart.top - 2) {\n add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);\n add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);\n } else {\n add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);\n }\n }\n if (leftEnd.bottom < rightStart.top)\n { add(leftSide, leftEnd.bottom, null, rightStart.top); }\n }\n\n output.appendChild(fragment);\n }\n\n // Cursor-blinking\n function restartBlink(cm) {\n if (!cm.state.focused) { return }\n var display = cm.display;\n clearInterval(display.blinker);\n var on = true;\n display.cursorDiv.style.visibility = \"\";\n if (cm.options.cursorBlinkRate > 0)\n { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? \"\" : \"hidden\"; },\n cm.options.cursorBlinkRate); }\n else if (cm.options.cursorBlinkRate < 0)\n { display.cursorDiv.style.visibility = \"hidden\"; }\n }\n\n function ensureFocus(cm) {\n if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }\n }\n\n function delayBlurEvent(cm) {\n cm.state.delayingBlurEvent = true;\n setTimeout(function () { if (cm.state.delayingBlurEvent) {\n cm.state.delayingBlurEvent = false;\n onBlur(cm);\n } }, 100);\n }\n\n function onFocus(cm, e) {\n if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; }\n\n if (cm.options.readOnly == \"nocursor\") { return }\n if (!cm.state.focused) {\n signal(cm, \"focus\", cm, e);\n cm.state.focused = true;\n addClass(cm.display.wrapper, \"CodeMirror-focused\");\n // This test prevents this from firing when a context\n // menu is closed (since the input reset would kill the\n // select-all detection hack)\n if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {\n cm.display.input.reset();\n if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730\n }\n cm.display.input.receivedFocus();\n }\n restartBlink(cm);\n }\n function onBlur(cm, e) {\n if (cm.state.delayingBlurEvent) { return }\n\n if (cm.state.focused) {\n signal(cm, \"blur\", cm, e);\n cm.state.focused = false;\n rmClass(cm.display.wrapper, \"CodeMirror-focused\");\n }\n clearInterval(cm.display.blinker);\n setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150);\n }\n\n // Read the actual heights of the rendered lines, and update their\n // stored heights to match.\n function updateHeightsInViewport(cm) {\n var display = cm.display;\n var prevBottom = display.lineDiv.offsetTop;\n for (var i = 0; i < display.view.length; i++) {\n var cur = display.view[i], wrapping = cm.options.lineWrapping;\n var height = (void 0), width = 0;\n if (cur.hidden) { continue }\n if (ie && ie_version < 8) {\n var bot = cur.node.offsetTop + cur.node.offsetHeight;\n height = bot - prevBottom;\n prevBottom = bot;\n } else {\n var box = cur.node.getBoundingClientRect();\n height = box.bottom - box.top;\n // Check that lines don't extend past the right of the current\n // editor width\n if (!wrapping && cur.text.firstChild)\n { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; }\n }\n var diff = cur.line.height - height;\n if (diff > .005 || diff < -.005) {\n updateLineHeight(cur.line, height);\n updateWidgetHeight(cur.line);\n if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)\n { updateWidgetHeight(cur.rest[j]); } }\n }\n if (width > cm.display.sizerWidth) {\n var chWidth = Math.ceil(width / charWidth(cm.display));\n if (chWidth > cm.display.maxLineLength) {\n cm.display.maxLineLength = chWidth;\n cm.display.maxLine = cur.line;\n cm.display.maxLineChanged = true;\n }\n }\n }\n }\n\n // Read and store the height of line widgets associated with the\n // given line.\n function updateWidgetHeight(line) {\n if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {\n var w = line.widgets[i], parent = w.node.parentNode;\n if (parent) { w.height = parent.offsetHeight; }\n } }\n }\n\n // Compute the lines that are visible in a given viewport (defaults\n // the the current scroll position). viewport may contain top,\n // height, and ensure (see op.scrollToPos) properties.\n function visibleLines(display, doc, viewport) {\n var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;\n top = Math.floor(top - paddingTop(display));\n var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;\n\n var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);\n // Ensure is a {from: {line, ch}, to: {line, ch}} object, and\n // forces those lines into the viewport (if possible).\n if (viewport && viewport.ensure) {\n var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;\n if (ensureFrom < from) {\n from = ensureFrom;\n to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);\n } else if (Math.min(ensureTo, doc.lastLine()) >= to) {\n from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);\n to = ensureTo;\n }\n }\n return {from: from, to: Math.max(to, from + 1)}\n }\n\n // SCROLLING THINGS INTO VIEW\n\n // If an editor sits on the top or bottom of the window, partially\n // scrolled out of view, this ensures that the cursor is visible.\n function maybeScrollWindow(cm, rect) {\n if (signalDOMEvent(cm, \"scrollCursorIntoView\")) { return }\n\n var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;\n if (rect.top + box.top < 0) { doScroll = true; }\n else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; }\n if (doScroll != null && !phantom) {\n var scrollNode = elt(\"div\", \"\\u200b\", null, (\"position: absolute;\\n top: \" + (rect.top - display.viewOffset - paddingTop(cm.display)) + \"px;\\n height: \" + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + \"px;\\n left: \" + (rect.left) + \"px; width: \" + (Math.max(2, rect.right - rect.left)) + \"px;\"));\n cm.display.lineSpace.appendChild(scrollNode);\n scrollNode.scrollIntoView(doScroll);\n cm.display.lineSpace.removeChild(scrollNode);\n }\n }\n\n // Scroll a given position into view (immediately), verifying that\n // it actually became visible (as line heights are accurately\n // measured, the position of something may 'drift' during drawing).\n function scrollPosIntoView(cm, pos, end, margin) {\n if (margin == null) { margin = 0; }\n var rect;\n if (!cm.options.lineWrapping && pos == end) {\n // Set pos and end to the cursor positions around the character pos sticks to\n // If pos.sticky == \"before\", that is around pos.ch - 1, otherwise around pos.ch\n // If pos == Pos(_, 0, \"before\"), pos and end are unchanged\n pos = pos.ch ? Pos(pos.line, pos.sticky == \"before\" ? pos.ch - 1 : pos.ch, \"after\") : pos;\n end = pos.sticky == \"before\" ? Pos(pos.line, pos.ch + 1, \"before\") : pos;\n }\n for (var limit = 0; limit < 5; limit++) {\n var changed = false;\n var coords = cursorCoords(cm, pos);\n var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);\n rect = {left: Math.min(coords.left, endCoords.left),\n top: Math.min(coords.top, endCoords.top) - margin,\n right: Math.max(coords.left, endCoords.left),\n bottom: Math.max(coords.bottom, endCoords.bottom) + margin};\n var scrollPos = calculateScrollPos(cm, rect);\n var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;\n if (scrollPos.scrollTop != null) {\n updateScrollTop(cm, scrollPos.scrollTop);\n if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; }\n }\n if (scrollPos.scrollLeft != null) {\n setScrollLeft(cm, scrollPos.scrollLeft);\n if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; }\n }\n if (!changed) { break }\n }\n return rect\n }\n\n // Scroll a given set of coordinates into view (immediately).\n function scrollIntoView(cm, rect) {\n var scrollPos = calculateScrollPos(cm, rect);\n if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); }\n if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); }\n }\n\n // Calculate a new scroll position needed to scroll the given\n // rectangle into view. Returns an object with scrollTop and\n // scrollLeft properties. When these are undefined, the\n // vertical/horizontal position does not need to be adjusted.\n function calculateScrollPos(cm, rect) {\n var display = cm.display, snapMargin = textHeight(cm.display);\n if (rect.top < 0) { rect.top = 0; }\n var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;\n var screen = displayHeight(cm), result = {};\n if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; }\n var docBottom = cm.doc.height + paddingVert(display);\n var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin;\n if (rect.top < screentop) {\n result.scrollTop = atTop ? 0 : rect.top;\n } else if (rect.bottom > screentop + screen) {\n var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen);\n if (newTop != screentop) { result.scrollTop = newTop; }\n }\n\n var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;\n var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);\n var tooWide = rect.right - rect.left > screenw;\n if (tooWide) { rect.right = rect.left + screenw; }\n if (rect.left < 10)\n { result.scrollLeft = 0; }\n else if (rect.left < screenleft)\n { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); }\n else if (rect.right > screenw + screenleft - 3)\n { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; }\n return result\n }\n\n // Store a relative adjustment to the scroll position in the current\n // operation (to be applied when the operation finishes).\n function addToScrollTop(cm, top) {\n if (top == null) { return }\n resolveScrollToPos(cm);\n cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;\n }\n\n // Make sure that at the end of the operation the current cursor is\n // shown.\n function ensureCursorVisible(cm) {\n resolveScrollToPos(cm);\n var cur = cm.getCursor();\n cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin};\n }\n\n function scrollToCoords(cm, x, y) {\n if (x != null || y != null) { resolveScrollToPos(cm); }\n if (x != null) { cm.curOp.scrollLeft = x; }\n if (y != null) { cm.curOp.scrollTop = y; }\n }\n\n function scrollToRange(cm, range) {\n resolveScrollToPos(cm);\n cm.curOp.scrollToPos = range;\n }\n\n // When an operation has its scrollToPos property set, and another\n // scroll action is applied before the end of the operation, this\n // 'simulates' scrolling that position into view in a cheap way, so\n // that the effect of intermediate scroll commands is not ignored.\n function resolveScrollToPos(cm) {\n var range = cm.curOp.scrollToPos;\n if (range) {\n cm.curOp.scrollToPos = null;\n var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to);\n scrollToCoordsRange(cm, from, to, range.margin);\n }\n }\n\n function scrollToCoordsRange(cm, from, to, margin) {\n var sPos = calculateScrollPos(cm, {\n left: Math.min(from.left, to.left),\n top: Math.min(from.top, to.top) - margin,\n right: Math.max(from.right, to.right),\n bottom: Math.max(from.bottom, to.bottom) + margin\n });\n scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop);\n }\n\n // Sync the scrollable area and scrollbars, ensure the viewport\n // covers the visible area.\n function updateScrollTop(cm, val) {\n if (Math.abs(cm.doc.scrollTop - val) < 2) { return }\n if (!gecko) { updateDisplaySimple(cm, {top: val}); }\n setScrollTop(cm, val, true);\n if (gecko) { updateDisplaySimple(cm); }\n startWorker(cm, 100);\n }\n\n function setScrollTop(cm, val, forceScroll) {\n val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val);\n if (cm.display.scroller.scrollTop == val && !forceScroll) { return }\n cm.doc.scrollTop = val;\n cm.display.scrollbars.setScrollTop(val);\n if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; }\n }\n\n // Sync scroller and scrollbar, ensure the gutter elements are\n // aligned.\n function setScrollLeft(cm, val, isScroller, forceScroll) {\n val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);\n if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }\n cm.doc.scrollLeft = val;\n alignHorizontally(cm);\n if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; }\n cm.display.scrollbars.setScrollLeft(val);\n }\n\n // SCROLLBARS\n\n // Prepare DOM reads needed to update the scrollbars. Done in one\n // shot to minimize update/measure roundtrips.\n function measureForScrollbars(cm) {\n var d = cm.display, gutterW = d.gutters.offsetWidth;\n var docH = Math.round(cm.doc.height + paddingVert(cm.display));\n return {\n clientHeight: d.scroller.clientHeight,\n viewHeight: d.wrapper.clientHeight,\n scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,\n viewWidth: d.wrapper.clientWidth,\n barLeft: cm.options.fixedGutter ? gutterW : 0,\n docHeight: docH,\n scrollHeight: docH + scrollGap(cm) + d.barHeight,\n nativeBarWidth: d.nativeBarWidth,\n gutterWidth: gutterW\n }\n }\n\n var NativeScrollbars = function(place, scroll, cm) {\n this.cm = cm;\n var vert = this.vert = elt(\"div\", [elt(\"div\", null, null, \"min-width: 1px\")], \"CodeMirror-vscrollbar\");\n var horiz = this.horiz = elt(\"div\", [elt(\"div\", null, null, \"height: 100%; min-height: 1px\")], \"CodeMirror-hscrollbar\");\n vert.tabIndex = horiz.tabIndex = -1;\n place(vert); place(horiz);\n\n on(vert, \"scroll\", function () {\n if (vert.clientHeight) { scroll(vert.scrollTop, \"vertical\"); }\n });\n on(horiz, \"scroll\", function () {\n if (horiz.clientWidth) { scroll(horiz.scrollLeft, \"horizontal\"); }\n });\n\n this.checkedZeroWidth = false;\n // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).\n if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = \"18px\"; }\n };\n\n NativeScrollbars.prototype.update = function (measure) {\n var needsH = measure.scrollWidth > measure.clientWidth + 1;\n var needsV = measure.scrollHeight > measure.clientHeight + 1;\n var sWidth = measure.nativeBarWidth;\n\n if (needsV) {\n this.vert.style.display = \"block\";\n this.vert.style.bottom = needsH ? sWidth + \"px\" : \"0\";\n var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);\n // A bug in IE8 can cause this value to be negative, so guard it.\n this.vert.firstChild.style.height =\n Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + \"px\";\n } else {\n this.vert.style.display = \"\";\n this.vert.firstChild.style.height = \"0\";\n }\n\n if (needsH) {\n this.horiz.style.display = \"block\";\n this.horiz.style.right = needsV ? sWidth + \"px\" : \"0\";\n this.horiz.style.left = measure.barLeft + \"px\";\n var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);\n this.horiz.firstChild.style.width =\n Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + \"px\";\n } else {\n this.horiz.style.display = \"\";\n this.horiz.firstChild.style.width = \"0\";\n }\n\n if (!this.checkedZeroWidth && measure.clientHeight > 0) {\n if (sWidth == 0) { this.zeroWidthHack(); }\n this.checkedZeroWidth = true;\n }\n\n return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}\n };\n\n NativeScrollbars.prototype.setScrollLeft = function (pos) {\n if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; }\n if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, \"horiz\"); }\n };\n\n NativeScrollbars.prototype.setScrollTop = function (pos) {\n if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; }\n if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, \"vert\"); }\n };\n\n NativeScrollbars.prototype.zeroWidthHack = function () {\n var w = mac && !mac_geMountainLion ? \"12px\" : \"18px\";\n this.horiz.style.height = this.vert.style.width = w;\n this.horiz.style.pointerEvents = this.vert.style.pointerEvents = \"none\";\n this.disableHoriz = new Delayed;\n this.disableVert = new Delayed;\n };\n\n NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {\n bar.style.pointerEvents = \"auto\";\n function maybeDisable() {\n // To find out whether the scrollbar is still visible, we\n // check whether the element under the pixel in the bottom\n // right corner of the scrollbar box is the scrollbar box\n // itself (when the bar is still visible) or its filler child\n // (when the bar is hidden). If it is still visible, we keep\n // it enabled, if it's hidden, we disable pointer events.\n var box = bar.getBoundingClientRect();\n var elt = type == \"vert\" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)\n : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1);\n if (elt != bar) { bar.style.pointerEvents = \"none\"; }\n else { delay.set(1000, maybeDisable); }\n }\n delay.set(1000, maybeDisable);\n };\n\n NativeScrollbars.prototype.clear = function () {\n var parent = this.horiz.parentNode;\n parent.removeChild(this.horiz);\n parent.removeChild(this.vert);\n };\n\n var NullScrollbars = function () {};\n\n NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };\n NullScrollbars.prototype.setScrollLeft = function () {};\n NullScrollbars.prototype.setScrollTop = function () {};\n NullScrollbars.prototype.clear = function () {};\n\n function updateScrollbars(cm, measure) {\n if (!measure) { measure = measureForScrollbars(cm); }\n var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;\n updateScrollbarsInner(cm, measure);\n for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {\n if (startWidth != cm.display.barWidth && cm.options.lineWrapping)\n { updateHeightsInViewport(cm); }\n updateScrollbarsInner(cm, measureForScrollbars(cm));\n startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;\n }\n }\n\n // Re-synchronize the fake scrollbars with the actual size of the\n // content.\n function updateScrollbarsInner(cm, measure) {\n var d = cm.display;\n var sizes = d.scrollbars.update(measure);\n\n d.sizer.style.paddingRight = (d.barWidth = sizes.right) + \"px\";\n d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + \"px\";\n d.heightForcer.style.borderBottom = sizes.bottom + \"px solid transparent\";\n\n if (sizes.right && sizes.bottom) {\n d.scrollbarFiller.style.display = \"block\";\n d.scrollbarFiller.style.height = sizes.bottom + \"px\";\n d.scrollbarFiller.style.width = sizes.right + \"px\";\n } else { d.scrollbarFiller.style.display = \"\"; }\n if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {\n d.gutterFiller.style.display = \"block\";\n d.gutterFiller.style.height = sizes.bottom + \"px\";\n d.gutterFiller.style.width = measure.gutterWidth + \"px\";\n } else { d.gutterFiller.style.display = \"\"; }\n }\n\n var scrollbarModel = {\"native\": NativeScrollbars, \"null\": NullScrollbars};\n\n function initScrollbars(cm) {\n if (cm.display.scrollbars) {\n cm.display.scrollbars.clear();\n if (cm.display.scrollbars.addClass)\n { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); }\n }\n\n cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {\n cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);\n // Prevent clicks in the scrollbars from killing focus\n on(node, \"mousedown\", function () {\n if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); }\n });\n node.setAttribute(\"cm-not-content\", \"true\");\n }, function (pos, axis) {\n if (axis == \"horizontal\") { setScrollLeft(cm, pos); }\n else { updateScrollTop(cm, pos); }\n }, cm);\n if (cm.display.scrollbars.addClass)\n { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); }\n }\n\n // Operations are used to wrap a series of changes to the editor\n // state in such a way that each change won't have to update the\n // cursor and display (which would be awkward, slow, and\n // error-prone). Instead, display updates are batched and then all\n // combined and executed at once.\n\n var nextOpId = 0;\n // Start a new operation.\n function startOperation(cm) {\n cm.curOp = {\n cm: cm,\n viewChanged: false, // Flag that indicates that lines might need to be redrawn\n startHeight: cm.doc.height, // Used to detect need to update scrollbar\n forceUpdate: false, // Used to force a redraw\n updateInput: 0, // Whether to reset the input textarea\n typing: false, // Whether this reset should be careful to leave existing text (for compositing)\n changeObjs: null, // Accumulated changes, for firing change events\n cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on\n cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already\n selectionChanged: false, // Whether the selection needs to be redrawn\n updateMaxLine: false, // Set when the widest line needs to be determined anew\n scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet\n scrollToPos: null, // Used to scroll to a specific position\n focus: false,\n id: ++nextOpId // Unique ID\n };\n pushOperation(cm.curOp);\n }\n\n // Finish an operation, updating the display and signalling delayed events\n function endOperation(cm) {\n var op = cm.curOp;\n if (op) { finishOperation(op, function (group) {\n for (var i = 0; i < group.ops.length; i++)\n { group.ops[i].cm.curOp = null; }\n endOperations(group);\n }); }\n }\n\n // The DOM updates done when an operation finishes are batched so\n // that the minimum number of relayouts are required.\n function endOperations(group) {\n var ops = group.ops;\n for (var i = 0; i < ops.length; i++) // Read DOM\n { endOperation_R1(ops[i]); }\n for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)\n { endOperation_W1(ops[i$1]); }\n for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM\n { endOperation_R2(ops[i$2]); }\n for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)\n { endOperation_W2(ops[i$3]); }\n for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM\n { endOperation_finish(ops[i$4]); }\n }\n\n function endOperation_R1(op) {\n var cm = op.cm, display = cm.display;\n maybeClipScrollbars(cm);\n if (op.updateMaxLine) { findMaxLine(cm); }\n\n op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||\n op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||\n op.scrollToPos.to.line >= display.viewTo) ||\n display.maxLineChanged && cm.options.lineWrapping;\n op.update = op.mustUpdate &&\n new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);\n }\n\n function endOperation_W1(op) {\n op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);\n }\n\n function endOperation_R2(op) {\n var cm = op.cm, display = cm.display;\n if (op.updatedDisplay) { updateHeightsInViewport(cm); }\n\n op.barMeasure = measureForScrollbars(cm);\n\n // If the max line changed since it was last measured, measure it,\n // and ensure the document's width matches it.\n // updateDisplay_W2 will use these properties to do the actual resizing\n if (display.maxLineChanged && !cm.options.lineWrapping) {\n op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;\n cm.display.sizerWidth = op.adjustWidthTo;\n op.barMeasure.scrollWidth =\n Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);\n op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));\n }\n\n if (op.updatedDisplay || op.selectionChanged)\n { op.preparedSelection = display.input.prepareSelection(); }\n }\n\n function endOperation_W2(op) {\n var cm = op.cm;\n\n if (op.adjustWidthTo != null) {\n cm.display.sizer.style.minWidth = op.adjustWidthTo + \"px\";\n if (op.maxScrollLeft < cm.doc.scrollLeft)\n { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); }\n cm.display.maxLineChanged = false;\n }\n\n var takeFocus = op.focus && op.focus == activeElt();\n if (op.preparedSelection)\n { cm.display.input.showSelection(op.preparedSelection, takeFocus); }\n if (op.updatedDisplay || op.startHeight != cm.doc.height)\n { updateScrollbars(cm, op.barMeasure); }\n if (op.updatedDisplay)\n { setDocumentHeight(cm, op.barMeasure); }\n\n if (op.selectionChanged) { restartBlink(cm); }\n\n if (cm.state.focused && op.updateInput)\n { cm.display.input.reset(op.typing); }\n if (takeFocus) { ensureFocus(op.cm); }\n }\n\n function endOperation_finish(op) {\n var cm = op.cm, display = cm.display, doc = cm.doc;\n\n if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); }\n\n // Abort mouse wheel delta measurement, when scrolling explicitly\n if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))\n { display.wheelStartX = display.wheelStartY = null; }\n\n // Propagate the scroll position to the actual DOM scroller\n if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); }\n\n if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); }\n // If we need to scroll a specific position into view, do so.\n if (op.scrollToPos) {\n var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),\n clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);\n maybeScrollWindow(cm, rect);\n }\n\n // Fire events for markers that are hidden/unidden by editing or\n // undoing\n var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;\n if (hidden) { for (var i = 0; i < hidden.length; ++i)\n { if (!hidden[i].lines.length) { signal(hidden[i], \"hide\"); } } }\n if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)\n { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], \"unhide\"); } } }\n\n if (display.wrapper.offsetHeight)\n { doc.scrollTop = cm.display.scroller.scrollTop; }\n\n // Fire change events, and delayed event handlers\n if (op.changeObjs)\n { signal(cm, \"changes\", cm, op.changeObjs); }\n if (op.update)\n { op.update.finish(); }\n }\n\n // Run the given function in an operation\n function runInOp(cm, f) {\n if (cm.curOp) { return f() }\n startOperation(cm);\n try { return f() }\n finally { endOperation(cm); }\n }\n // Wraps a function in an operation. Returns the wrapped function.\n function operation(cm, f) {\n return function() {\n if (cm.curOp) { return f.apply(cm, arguments) }\n startOperation(cm);\n try { return f.apply(cm, arguments) }\n finally { endOperation(cm); }\n }\n }\n // Used to add methods to editor and doc instances, wrapping them in\n // operations.\n function methodOp(f) {\n return function() {\n if (this.curOp) { return f.apply(this, arguments) }\n startOperation(this);\n try { return f.apply(this, arguments) }\n finally { endOperation(this); }\n }\n }\n function docMethodOp(f) {\n return function() {\n var cm = this.cm;\n if (!cm || cm.curOp) { return f.apply(this, arguments) }\n startOperation(cm);\n try { return f.apply(this, arguments) }\n finally { endOperation(cm); }\n }\n }\n\n // HIGHLIGHT WORKER\n\n function startWorker(cm, time) {\n if (cm.doc.highlightFrontier < cm.display.viewTo)\n { cm.state.highlight.set(time, bind(highlightWorker, cm)); }\n }\n\n function highlightWorker(cm) {\n var doc = cm.doc;\n if (doc.highlightFrontier >= cm.display.viewTo) { return }\n var end = +new Date + cm.options.workTime;\n var context = getContextBefore(cm, doc.highlightFrontier);\n var changedLines = [];\n\n doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {\n if (context.line >= cm.display.viewFrom) { // Visible\n var oldStyles = line.styles;\n var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null;\n var highlighted = highlightLine(cm, line, context, true);\n if (resetState) { context.state = resetState; }\n line.styles = highlighted.styles;\n var oldCls = line.styleClasses, newCls = highlighted.classes;\n if (newCls) { line.styleClasses = newCls; }\n else if (oldCls) { line.styleClasses = null; }\n var ischange = !oldStyles || oldStyles.length != line.styles.length ||\n oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);\n for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; }\n if (ischange) { changedLines.push(context.line); }\n line.stateAfter = context.save();\n context.nextLine();\n } else {\n if (line.text.length <= cm.options.maxHighlightLength)\n { processLine(cm, line.text, context); }\n line.stateAfter = context.line % 5 == 0 ? context.save() : null;\n context.nextLine();\n }\n if (+new Date > end) {\n startWorker(cm, cm.options.workDelay);\n return true\n }\n });\n doc.highlightFrontier = context.line;\n doc.modeFrontier = Math.max(doc.modeFrontier, context.line);\n if (changedLines.length) { runInOp(cm, function () {\n for (var i = 0; i < changedLines.length; i++)\n { regLineChange(cm, changedLines[i], \"text\"); }\n }); }\n }\n\n // DISPLAY DRAWING\n\n var DisplayUpdate = function(cm, viewport, force) {\n var display = cm.display;\n\n this.viewport = viewport;\n // Store some values that we'll need later (but don't want to force a relayout for)\n this.visible = visibleLines(display, cm.doc, viewport);\n this.editorIsHidden = !display.wrapper.offsetWidth;\n this.wrapperHeight = display.wrapper.clientHeight;\n this.wrapperWidth = display.wrapper.clientWidth;\n this.oldDisplayWidth = displayWidth(cm);\n this.force = force;\n this.dims = getDimensions(cm);\n this.events = [];\n };\n\n DisplayUpdate.prototype.signal = function (emitter, type) {\n if (hasHandler(emitter, type))\n { this.events.push(arguments); }\n };\n DisplayUpdate.prototype.finish = function () {\n for (var i = 0; i < this.events.length; i++)\n { signal.apply(null, this.events[i]); }\n };\n\n function maybeClipScrollbars(cm) {\n var display = cm.display;\n if (!display.scrollbarsClipped && display.scroller.offsetWidth) {\n display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;\n display.heightForcer.style.height = scrollGap(cm) + \"px\";\n display.sizer.style.marginBottom = -display.nativeBarWidth + \"px\";\n display.sizer.style.borderRightWidth = scrollGap(cm) + \"px\";\n display.scrollbarsClipped = true;\n }\n }\n\n function selectionSnapshot(cm) {\n if (cm.hasFocus()) { return null }\n var active = activeElt();\n if (!active || !contains(cm.display.lineDiv, active)) { return null }\n var result = {activeElt: active};\n if (window.getSelection) {\n var sel = window.getSelection();\n if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {\n result.anchorNode = sel.anchorNode;\n result.anchorOffset = sel.anchorOffset;\n result.focusNode = sel.focusNode;\n result.focusOffset = sel.focusOffset;\n }\n }\n return result\n }\n\n function restoreSelection(snapshot) {\n if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }\n snapshot.activeElt.focus();\n if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {\n var sel = window.getSelection(), range = document.createRange();\n range.setEnd(snapshot.anchorNode, snapshot.anchorOffset);\n range.collapse(false);\n sel.removeAllRanges();\n sel.addRange(range);\n sel.extend(snapshot.focusNode, snapshot.focusOffset);\n }\n }\n\n // Does the actual updating of the line display. Bails out\n // (returning false) when there is nothing to be done and forced is\n // false.\n function updateDisplayIfNeeded(cm, update) {\n var display = cm.display, doc = cm.doc;\n\n if (update.editorIsHidden) {\n resetView(cm);\n return false\n }\n\n // Bail out if the visible area is already rendered and nothing changed.\n if (!update.force &&\n update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&\n (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&\n display.renderedView == display.view && countDirtyView(cm) == 0)\n { return false }\n\n if (maybeUpdateLineNumberWidth(cm)) {\n resetView(cm);\n update.dims = getDimensions(cm);\n }\n\n // Compute a suitable new viewport (from & to)\n var end = doc.first + doc.size;\n var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);\n var to = Math.min(end, update.visible.to + cm.options.viewportMargin);\n if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); }\n if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); }\n if (sawCollapsedSpans) {\n from = visualLineNo(cm.doc, from);\n to = visualLineEndNo(cm.doc, to);\n }\n\n var different = from != display.viewFrom || to != display.viewTo ||\n display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;\n adjustView(cm, from, to);\n\n display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));\n // Position the mover div to align with the current scroll position\n cm.display.mover.style.top = display.viewOffset + \"px\";\n\n var toUpdate = countDirtyView(cm);\n if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&\n (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))\n { return false }\n\n // For big changes, we hide the enclosing element during the\n // update, since that speeds up the operations on most browsers.\n var selSnapshot = selectionSnapshot(cm);\n if (toUpdate > 4) { display.lineDiv.style.display = \"none\"; }\n patchDisplay(cm, display.updateLineNumbers, update.dims);\n if (toUpdate > 4) { display.lineDiv.style.display = \"\"; }\n display.renderedView = display.view;\n // There might have been a widget with a focused element that got\n // hidden or updated, if so re-focus it.\n restoreSelection(selSnapshot);\n\n // Prevent selection and cursors from interfering with the scroll\n // width and height.\n removeChildren(display.cursorDiv);\n removeChildren(display.selectionDiv);\n display.gutters.style.height = display.sizer.style.minHeight = 0;\n\n if (different) {\n display.lastWrapHeight = update.wrapperHeight;\n display.lastWrapWidth = update.wrapperWidth;\n startWorker(cm, 400);\n }\n\n display.updateLineNumbers = null;\n\n return true\n }\n\n function postUpdateDisplay(cm, update) {\n var viewport = update.viewport;\n\n for (var first = true;; first = false) {\n if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {\n // Clip forced viewport to actual scrollable area.\n if (viewport && viewport.top != null)\n { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; }\n // Updated line heights might result in the drawn area not\n // actually covering the viewport. Keep looping until it does.\n update.visible = visibleLines(cm.display, cm.doc, viewport);\n if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)\n { break }\n }\n if (!updateDisplayIfNeeded(cm, update)) { break }\n updateHeightsInViewport(cm);\n var barMeasure = measureForScrollbars(cm);\n updateSelection(cm);\n updateScrollbars(cm, barMeasure);\n setDocumentHeight(cm, barMeasure);\n update.force = false;\n }\n\n update.signal(cm, \"update\", cm);\n if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {\n update.signal(cm, \"viewportChange\", cm, cm.display.viewFrom, cm.display.viewTo);\n cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;\n }\n }\n\n function updateDisplaySimple(cm, viewport) {\n var update = new DisplayUpdate(cm, viewport);\n if (updateDisplayIfNeeded(cm, update)) {\n updateHeightsInViewport(cm);\n postUpdateDisplay(cm, update);\n var barMeasure = measureForScrollbars(cm);\n updateSelection(cm);\n updateScrollbars(cm, barMeasure);\n setDocumentHeight(cm, barMeasure);\n update.finish();\n }\n }\n\n // Sync the actual display DOM structure with display.view, removing\n // nodes for lines that are no longer in view, and creating the ones\n // that are not there yet, and updating the ones that are out of\n // date.\n function patchDisplay(cm, updateNumbersFrom, dims) {\n var display = cm.display, lineNumbers = cm.options.lineNumbers;\n var container = display.lineDiv, cur = container.firstChild;\n\n function rm(node) {\n var next = node.nextSibling;\n // Works around a throw-scroll bug in OS X Webkit\n if (webkit && mac && cm.display.currentWheelTarget == node)\n { node.style.display = \"none\"; }\n else\n { node.parentNode.removeChild(node); }\n return next\n }\n\n var view = display.view, lineN = display.viewFrom;\n // Loop over the elements in the view, syncing cur (the DOM nodes\n // in display.lineDiv) with the view as we go.\n for (var i = 0; i < view.length; i++) {\n var lineView = view[i];\n if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet\n var node = buildLineElement(cm, lineView, lineN, dims);\n container.insertBefore(node, cur);\n } else { // Already drawn\n while (cur != lineView.node) { cur = rm(cur); }\n var updateNumber = lineNumbers && updateNumbersFrom != null &&\n updateNumbersFrom <= lineN && lineView.lineNumber;\n if (lineView.changes) {\n if (indexOf(lineView.changes, \"gutter\") > -1) { updateNumber = false; }\n updateLineForChanges(cm, lineView, lineN, dims);\n }\n if (updateNumber) {\n removeChildren(lineView.lineNumber);\n lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));\n }\n cur = lineView.node.nextSibling;\n }\n lineN += lineView.size;\n }\n while (cur) { cur = rm(cur); }\n }\n\n function updateGutterSpace(display) {\n var width = display.gutters.offsetWidth;\n display.sizer.style.marginLeft = width + \"px\";\n }\n\n function setDocumentHeight(cm, measure) {\n cm.display.sizer.style.minHeight = measure.docHeight + \"px\";\n cm.display.heightForcer.style.top = measure.docHeight + \"px\";\n cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + \"px\";\n }\n\n // Re-align line numbers and gutter marks to compensate for\n // horizontal scrolling.\n function alignHorizontally(cm) {\n var display = cm.display, view = display.view;\n if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }\n var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;\n var gutterW = display.gutters.offsetWidth, left = comp + \"px\";\n for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {\n if (cm.options.fixedGutter) {\n if (view[i].gutter)\n { view[i].gutter.style.left = left; }\n if (view[i].gutterBackground)\n { view[i].gutterBackground.style.left = left; }\n }\n var align = view[i].alignable;\n if (align) { for (var j = 0; j < align.length; j++)\n { align[j].style.left = left; } }\n } }\n if (cm.options.fixedGutter)\n { display.gutters.style.left = (comp + gutterW) + \"px\"; }\n }\n\n // Used to ensure that the line number gutter is still the right\n // size for the current document size. Returns true when an update\n // is needed.\n function maybeUpdateLineNumberWidth(cm) {\n if (!cm.options.lineNumbers) { return false }\n var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;\n if (last.length != display.lineNumChars) {\n var test = display.measure.appendChild(elt(\"div\", [elt(\"div\", last)],\n \"CodeMirror-linenumber CodeMirror-gutter-elt\"));\n var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;\n display.lineGutter.style.width = \"\";\n display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;\n display.lineNumWidth = display.lineNumInnerWidth + padding;\n display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;\n display.lineGutter.style.width = display.lineNumWidth + \"px\";\n updateGutterSpace(cm.display);\n return true\n }\n return false\n }\n\n function getGutters(gutters, lineNumbers) {\n var result = [], sawLineNumbers = false;\n for (var i = 0; i < gutters.length; i++) {\n var name = gutters[i], style = null;\n if (typeof name != \"string\") { style = name.style; name = name.className; }\n if (name == \"CodeMirror-linenumbers\") {\n if (!lineNumbers) { continue }\n else { sawLineNumbers = true; }\n }\n result.push({className: name, style: style});\n }\n if (lineNumbers && !sawLineNumbers) { result.push({className: \"CodeMirror-linenumbers\", style: null}); }\n return result\n }\n\n // Rebuild the gutter elements, ensure the margin to the left of the\n // code matches their width.\n function renderGutters(display) {\n var gutters = display.gutters, specs = display.gutterSpecs;\n removeChildren(gutters);\n display.lineGutter = null;\n for (var i = 0; i < specs.length; ++i) {\n var ref = specs[i];\n var className = ref.className;\n var style = ref.style;\n var gElt = gutters.appendChild(elt(\"div\", null, \"CodeMirror-gutter \" + className));\n if (style) { gElt.style.cssText = style; }\n if (className == \"CodeMirror-linenumbers\") {\n display.lineGutter = gElt;\n gElt.style.width = (display.lineNumWidth || 1) + \"px\";\n }\n }\n gutters.style.display = specs.length ? \"\" : \"none\";\n updateGutterSpace(display);\n }\n\n function updateGutters(cm) {\n renderGutters(cm.display);\n regChange(cm);\n alignHorizontally(cm);\n }\n\n // The display handles the DOM integration, both for input reading\n // and content drawing. It holds references to DOM nodes and\n // display-related state.\n\n function Display(place, doc, input, options) {\n var d = this;\n this.input = input;\n\n // Covers bottom-right square when both scrollbars are present.\n d.scrollbarFiller = elt(\"div\", null, \"CodeMirror-scrollbar-filler\");\n d.scrollbarFiller.setAttribute(\"cm-not-content\", \"true\");\n // Covers bottom of gutter when coverGutterNextToScrollbar is on\n // and h scrollbar is present.\n d.gutterFiller = elt(\"div\", null, \"CodeMirror-gutter-filler\");\n d.gutterFiller.setAttribute(\"cm-not-content\", \"true\");\n // Will contain the actual code, positioned to cover the viewport.\n d.lineDiv = eltP(\"div\", null, \"CodeMirror-code\");\n // Elements are added to these to represent selection and cursors.\n d.selectionDiv = elt(\"div\", null, null, \"position: relative; z-index: 1\");\n d.cursorDiv = elt(\"div\", null, \"CodeMirror-cursors\");\n // A visibility: hidden element used to find the size of things.\n d.measure = elt(\"div\", null, \"CodeMirror-measure\");\n // When lines outside of the viewport are measured, they are drawn in this.\n d.lineMeasure = elt(\"div\", null, \"CodeMirror-measure\");\n // Wraps everything that needs to exist inside the vertically-padded coordinate system\n d.lineSpace = eltP(\"div\", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],\n null, \"position: relative; outline: none\");\n var lines = eltP(\"div\", [d.lineSpace], \"CodeMirror-lines\");\n // Moved around its parent to cover visible view.\n d.mover = elt(\"div\", [lines], null, \"position: relative\");\n // Set to the height of the document, allowing scrolling.\n d.sizer = elt(\"div\", [d.mover], \"CodeMirror-sizer\");\n d.sizerWidth = null;\n // Behavior of elts with overflow: auto and padding is\n // inconsistent across browsers. This is used to ensure the\n // scrollable area is big enough.\n d.heightForcer = elt(\"div\", null, null, \"position: absolute; height: \" + scrollerGap + \"px; width: 1px;\");\n // Will contain the gutters, if any.\n d.gutters = elt(\"div\", null, \"CodeMirror-gutters\");\n d.lineGutter = null;\n // Actual scrollable element.\n d.scroller = elt(\"div\", [d.sizer, d.heightForcer, d.gutters], \"CodeMirror-scroll\");\n d.scroller.setAttribute(\"tabIndex\", \"-1\");\n // The element in which the editor lives.\n d.wrapper = elt(\"div\", [d.scrollbarFiller, d.gutterFiller, d.scroller], \"CodeMirror\");\n\n // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)\n if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }\n if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; }\n\n if (place) {\n if (place.appendChild) { place.appendChild(d.wrapper); }\n else { place(d.wrapper); }\n }\n\n // Current rendered range (may be bigger than the view window).\n d.viewFrom = d.viewTo = doc.first;\n d.reportedViewFrom = d.reportedViewTo = doc.first;\n // Information about the rendered lines.\n d.view = [];\n d.renderedView = null;\n // Holds info about a single rendered line when it was rendered\n // for measurement, while not in view.\n d.externalMeasured = null;\n // Empty space (in pixels) above the view\n d.viewOffset = 0;\n d.lastWrapHeight = d.lastWrapWidth = 0;\n d.updateLineNumbers = null;\n\n d.nativeBarWidth = d.barHeight = d.barWidth = 0;\n d.scrollbarsClipped = false;\n\n // Used to only resize the line number gutter when necessary (when\n // the amount of lines crosses a boundary that makes its width change)\n d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;\n // Set to true when a non-horizontal-scrolling line widget is\n // added. As an optimization, line widget aligning is skipped when\n // this is false.\n d.alignWidgets = false;\n\n d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;\n\n // Tracks the maximum line length so that the horizontal scrollbar\n // can be kept static when scrolling.\n d.maxLine = null;\n d.maxLineLength = 0;\n d.maxLineChanged = false;\n\n // Used for measuring wheel scrolling granularity\n d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;\n\n // True when shift is held down.\n d.shift = false;\n\n // Used to track whether anything happened since the context menu\n // was opened.\n d.selForContextMenu = null;\n\n d.activeTouch = null;\n\n d.gutterSpecs = getGutters(options.gutters, options.lineNumbers);\n renderGutters(d);\n\n input.init(d);\n }\n\n // Since the delta values reported on mouse wheel events are\n // unstandardized between browsers and even browser versions, and\n // generally horribly unpredictable, this code starts by measuring\n // the scroll effect that the first few mouse wheel events have,\n // and, from that, detects the way it can convert deltas to pixel\n // offsets afterwards.\n //\n // The reason we want to know the amount a wheel event will scroll\n // is that it gives us a chance to update the display before the\n // actual scrolling happens, reducing flickering.\n\n var wheelSamples = 0, wheelPixelsPerUnit = null;\n // Fill in a browser-detected starting value on browsers where we\n // know one. These don't have to be accurate -- the result of them\n // being wrong would just be a slight flicker on the first wheel\n // scroll (if it is large enough).\n if (ie) { wheelPixelsPerUnit = -.53; }\n else if (gecko) { wheelPixelsPerUnit = 15; }\n else if (chrome) { wheelPixelsPerUnit = -.7; }\n else if (safari) { wheelPixelsPerUnit = -1/3; }\n\n function wheelEventDelta(e) {\n var dx = e.wheelDeltaX, dy = e.wheelDeltaY;\n if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; }\n if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; }\n else if (dy == null) { dy = e.wheelDelta; }\n return {x: dx, y: dy}\n }\n function wheelEventPixels(e) {\n var delta = wheelEventDelta(e);\n delta.x *= wheelPixelsPerUnit;\n delta.y *= wheelPixelsPerUnit;\n return delta\n }\n\n function onScrollWheel(cm, e) {\n var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;\n\n var display = cm.display, scroll = display.scroller;\n // Quit if there's nothing to scroll here\n var canScrollX = scroll.scrollWidth > scroll.clientWidth;\n var canScrollY = scroll.scrollHeight > scroll.clientHeight;\n if (!(dx && canScrollX || dy && canScrollY)) { return }\n\n // Webkit browsers on OS X abort momentum scrolls when the target\n // of the scroll event is removed from the scrollable element.\n // This hack (see related code in patchDisplay) makes sure the\n // element is kept around.\n if (dy && mac && webkit) {\n outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {\n for (var i = 0; i < view.length; i++) {\n if (view[i].node == cur) {\n cm.display.currentWheelTarget = cur;\n break outer\n }\n }\n }\n }\n\n // On some browsers, horizontal scrolling will cause redraws to\n // happen before the gutter has been realigned, causing it to\n // wriggle around in a most unseemly way. When we have an\n // estimated pixels/delta value, we just handle horizontal\n // scrolling entirely here. It'll be slightly off from native, but\n // better than glitching out.\n if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {\n if (dy && canScrollY)\n { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); }\n setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit));\n // Only prevent default scrolling if vertical scrolling is\n // actually possible. Otherwise, it causes vertical scroll\n // jitter on OSX trackpads when deltaX is small and deltaY\n // is large (issue #3579)\n if (!dy || (dy && canScrollY))\n { e_preventDefault(e); }\n display.wheelStartX = null; // Abort measurement, if in progress\n return\n }\n\n // 'Project' the visible viewport to cover the area that is being\n // scrolled into view (if we know enough to estimate it).\n if (dy && wheelPixelsPerUnit != null) {\n var pixels = dy * wheelPixelsPerUnit;\n var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;\n if (pixels < 0) { top = Math.max(0, top + pixels - 50); }\n else { bot = Math.min(cm.doc.height, bot + pixels + 50); }\n updateDisplaySimple(cm, {top: top, bottom: bot});\n }\n\n if (wheelSamples < 20) {\n if (display.wheelStartX == null) {\n display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;\n display.wheelDX = dx; display.wheelDY = dy;\n setTimeout(function () {\n if (display.wheelStartX == null) { return }\n var movedX = scroll.scrollLeft - display.wheelStartX;\n var movedY = scroll.scrollTop - display.wheelStartY;\n var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||\n (movedX && display.wheelDX && movedX / display.wheelDX);\n display.wheelStartX = display.wheelStartY = null;\n if (!sample) { return }\n wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);\n ++wheelSamples;\n }, 200);\n } else {\n display.wheelDX += dx; display.wheelDY += dy;\n }\n }\n }\n\n // Selection objects are immutable. A new one is created every time\n // the selection changes. A selection is one or more non-overlapping\n // (and non-touching) ranges, sorted, and an integer that indicates\n // which one is the primary selection (the one that's scrolled into\n // view, that getCursor returns, etc).\n var Selection = function(ranges, primIndex) {\n this.ranges = ranges;\n this.primIndex = primIndex;\n };\n\n Selection.prototype.primary = function () { return this.ranges[this.primIndex] };\n\n Selection.prototype.equals = function (other) {\n if (other == this) { return true }\n if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }\n for (var i = 0; i < this.ranges.length; i++) {\n var here = this.ranges[i], there = other.ranges[i];\n if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }\n }\n return true\n };\n\n Selection.prototype.deepCopy = function () {\n var out = [];\n for (var i = 0; i < this.ranges.length; i++)\n { out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); }\n return new Selection(out, this.primIndex)\n };\n\n Selection.prototype.somethingSelected = function () {\n for (var i = 0; i < this.ranges.length; i++)\n { if (!this.ranges[i].empty()) { return true } }\n return false\n };\n\n Selection.prototype.contains = function (pos, end) {\n if (!end) { end = pos; }\n for (var i = 0; i < this.ranges.length; i++) {\n var range = this.ranges[i];\n if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)\n { return i }\n }\n return -1\n };\n\n var Range = function(anchor, head) {\n this.anchor = anchor; this.head = head;\n };\n\n Range.prototype.from = function () { return minPos(this.anchor, this.head) };\n Range.prototype.to = function () { return maxPos(this.anchor, this.head) };\n Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };\n\n // Take an unsorted, potentially overlapping set of ranges, and\n // build a selection out of it. 'Consumes' ranges array (modifying\n // it).\n function normalizeSelection(cm, ranges, primIndex) {\n var mayTouch = cm && cm.options.selectionsMayTouch;\n var prim = ranges[primIndex];\n ranges.sort(function (a, b) { return cmp(a.from(), b.from()); });\n primIndex = indexOf(ranges, prim);\n for (var i = 1; i < ranges.length; i++) {\n var cur = ranges[i], prev = ranges[i - 1];\n var diff = cmp(prev.to(), cur.from());\n if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) {\n var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());\n var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;\n if (i <= primIndex) { --primIndex; }\n ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));\n }\n }\n return new Selection(ranges, primIndex)\n }\n\n function simpleSelection(anchor, head) {\n return new Selection([new Range(anchor, head || anchor)], 0)\n }\n\n // Compute the position of the end of a change (its 'to' property\n // refers to the pre-change end).\n function changeEnd(change) {\n if (!change.text) { return change.to }\n return Pos(change.from.line + change.text.length - 1,\n lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))\n }\n\n // Adjust a position to refer to the post-change position of the\n // same text, or the end of the change if the change covers it.\n function adjustForChange(pos, change) {\n if (cmp(pos, change.from) < 0) { return pos }\n if (cmp(pos, change.to) <= 0) { return changeEnd(change) }\n\n var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;\n if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; }\n return Pos(line, ch)\n }\n\n function computeSelAfterChange(doc, change) {\n var out = [];\n for (var i = 0; i < doc.sel.ranges.length; i++) {\n var range = doc.sel.ranges[i];\n out.push(new Range(adjustForChange(range.anchor, change),\n adjustForChange(range.head, change)));\n }\n return normalizeSelection(doc.cm, out, doc.sel.primIndex)\n }\n\n function offsetPos(pos, old, nw) {\n if (pos.line == old.line)\n { return Pos(nw.line, pos.ch - old.ch + nw.ch) }\n else\n { return Pos(nw.line + (pos.line - old.line), pos.ch) }\n }\n\n // Used by replaceSelections to allow moving the selection to the\n // start or around the replaced test. Hint may be \"start\" or \"around\".\n function computeReplacedSel(doc, changes, hint) {\n var out = [];\n var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;\n for (var i = 0; i < changes.length; i++) {\n var change = changes[i];\n var from = offsetPos(change.from, oldPrev, newPrev);\n var to = offsetPos(changeEnd(change), oldPrev, newPrev);\n oldPrev = change.to;\n newPrev = to;\n if (hint == \"around\") {\n var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;\n out[i] = new Range(inv ? to : from, inv ? from : to);\n } else {\n out[i] = new Range(from, from);\n }\n }\n return new Selection(out, doc.sel.primIndex)\n }\n\n // Used to get the editor into a consistent state again when options change.\n\n function loadMode(cm) {\n cm.doc.mode = getMode(cm.options, cm.doc.modeOption);\n resetModeState(cm);\n }\n\n function resetModeState(cm) {\n cm.doc.iter(function (line) {\n if (line.stateAfter) { line.stateAfter = null; }\n if (line.styles) { line.styles = null; }\n });\n cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first;\n startWorker(cm, 100);\n cm.state.modeGen++;\n if (cm.curOp) { regChange(cm); }\n }\n\n // DOCUMENT DATA STRUCTURE\n\n // By default, updates that start and end at the beginning of a line\n // are treated specially, in order to make the association of line\n // widgets and marker elements with the text behave more intuitive.\n function isWholeLineUpdate(doc, change) {\n return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == \"\" &&\n (!doc.cm || doc.cm.options.wholeLineUpdateBefore)\n }\n\n // Perform a change on the document data structure.\n function updateDoc(doc, change, markedSpans, estimateHeight) {\n function spansFor(n) {return markedSpans ? markedSpans[n] : null}\n function update(line, text, spans) {\n updateLine(line, text, spans, estimateHeight);\n signalLater(line, \"change\", line, change);\n }\n function linesFor(start, end) {\n var result = [];\n for (var i = start; i < end; ++i)\n { result.push(new Line(text[i], spansFor(i), estimateHeight)); }\n return result\n }\n\n var from = change.from, to = change.to, text = change.text;\n var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);\n var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;\n\n // Adjust the line structure\n if (change.full) {\n doc.insert(0, linesFor(0, text.length));\n doc.remove(text.length, doc.size - text.length);\n } else if (isWholeLineUpdate(doc, change)) {\n // This is a whole-line replace. Treated specially to make\n // sure line objects move the way they are supposed to.\n var added = linesFor(0, text.length - 1);\n update(lastLine, lastLine.text, lastSpans);\n if (nlines) { doc.remove(from.line, nlines); }\n if (added.length) { doc.insert(from.line, added); }\n } else if (firstLine == lastLine) {\n if (text.length == 1) {\n update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);\n } else {\n var added$1 = linesFor(1, text.length - 1);\n added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));\n update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));\n doc.insert(from.line + 1, added$1);\n }\n } else if (text.length == 1) {\n update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));\n doc.remove(from.line + 1, nlines);\n } else {\n update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));\n update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);\n var added$2 = linesFor(1, text.length - 1);\n if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); }\n doc.insert(from.line + 1, added$2);\n }\n\n signalLater(doc, \"change\", doc, change);\n }\n\n // Call f for all linked documents.\n function linkedDocs(doc, f, sharedHistOnly) {\n function propagate(doc, skip, sharedHist) {\n if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {\n var rel = doc.linked[i];\n if (rel.doc == skip) { continue }\n var shared = sharedHist && rel.sharedHist;\n if (sharedHistOnly && !shared) { continue }\n f(rel.doc, shared);\n propagate(rel.doc, doc, shared);\n } }\n }\n propagate(doc, null, true);\n }\n\n // Attach a document to an editor.\n function attachDoc(cm, doc) {\n if (doc.cm) { throw new Error(\"This document is already in use.\") }\n cm.doc = doc;\n doc.cm = cm;\n estimateLineHeights(cm);\n loadMode(cm);\n setDirectionClass(cm);\n if (!cm.options.lineWrapping) { findMaxLine(cm); }\n cm.options.mode = doc.modeOption;\n regChange(cm);\n }\n\n function setDirectionClass(cm) {\n (cm.doc.direction == \"rtl\" ? addClass : rmClass)(cm.display.lineDiv, \"CodeMirror-rtl\");\n }\n\n function directionChanged(cm) {\n runInOp(cm, function () {\n setDirectionClass(cm);\n regChange(cm);\n });\n }\n\n function History(startGen) {\n // Arrays of change events and selections. Doing something adds an\n // event to done and clears undo. Undoing moves events from done\n // to undone, redoing moves them in the other direction.\n this.done = []; this.undone = [];\n this.undoDepth = Infinity;\n // Used to track when changes can be merged into a single undo\n // event\n this.lastModTime = this.lastSelTime = 0;\n this.lastOp = this.lastSelOp = null;\n this.lastOrigin = this.lastSelOrigin = null;\n // Used by the isClean() method\n this.generation = this.maxGeneration = startGen || 1;\n }\n\n // Create a history change event from an updateDoc-style change\n // object.\n function historyChangeFromChange(doc, change) {\n var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};\n attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);\n linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true);\n return histChange\n }\n\n // Pop all selection events off the end of a history array. Stop at\n // a change event.\n function clearSelectionEvents(array) {\n while (array.length) {\n var last = lst(array);\n if (last.ranges) { array.pop(); }\n else { break }\n }\n }\n\n // Find the top change event in the history. Pop off selection\n // events that are in the way.\n function lastChangeEvent(hist, force) {\n if (force) {\n clearSelectionEvents(hist.done);\n return lst(hist.done)\n } else if (hist.done.length && !lst(hist.done).ranges) {\n return lst(hist.done)\n } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {\n hist.done.pop();\n return lst(hist.done)\n }\n }\n\n // Register a change in the history. Merges changes that are within\n // a single operation, or are close together with an origin that\n // allows merging (starting with \"+\") into a single event.\n function addChangeToHistory(doc, change, selAfter, opId) {\n var hist = doc.history;\n hist.undone.length = 0;\n var time = +new Date, cur;\n var last;\n\n if ((hist.lastOp == opId ||\n hist.lastOrigin == change.origin && change.origin &&\n ((change.origin.charAt(0) == \"+\" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||\n change.origin.charAt(0) == \"*\")) &&\n (cur = lastChangeEvent(hist, hist.lastOp == opId))) {\n // Merge this change into the last event\n last = lst(cur.changes);\n if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {\n // Optimized case for simple insertion -- don't want to add\n // new changesets for every character typed\n last.to = changeEnd(change);\n } else {\n // Add new sub-event\n cur.changes.push(historyChangeFromChange(doc, change));\n }\n } else {\n // Can not be merged, start a new event.\n var before = lst(hist.done);\n if (!before || !before.ranges)\n { pushSelectionToHistory(doc.sel, hist.done); }\n cur = {changes: [historyChangeFromChange(doc, change)],\n generation: hist.generation};\n hist.done.push(cur);\n while (hist.done.length > hist.undoDepth) {\n hist.done.shift();\n if (!hist.done[0].ranges) { hist.done.shift(); }\n }\n }\n hist.done.push(selAfter);\n hist.generation = ++hist.maxGeneration;\n hist.lastModTime = hist.lastSelTime = time;\n hist.lastOp = hist.lastSelOp = opId;\n hist.lastOrigin = hist.lastSelOrigin = change.origin;\n\n if (!last) { signal(doc, \"historyAdded\"); }\n }\n\n function selectionEventCanBeMerged(doc, origin, prev, sel) {\n var ch = origin.charAt(0);\n return ch == \"*\" ||\n ch == \"+\" &&\n prev.ranges.length == sel.ranges.length &&\n prev.somethingSelected() == sel.somethingSelected() &&\n new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)\n }\n\n // Called whenever the selection changes, sets the new selection as\n // the pending selection in the history, and pushes the old pending\n // selection into the 'done' array when it was significantly\n // different (in number of selected ranges, emptiness, or time).\n function addSelectionToHistory(doc, sel, opId, options) {\n var hist = doc.history, origin = options && options.origin;\n\n // A new event is started when the previous origin does not match\n // the current, or the origins don't allow matching. Origins\n // starting with * are always merged, those starting with + are\n // merged when similar and close together in time.\n if (opId == hist.lastSelOp ||\n (origin && hist.lastSelOrigin == origin &&\n (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||\n selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))\n { hist.done[hist.done.length - 1] = sel; }\n else\n { pushSelectionToHistory(sel, hist.done); }\n\n hist.lastSelTime = +new Date;\n hist.lastSelOrigin = origin;\n hist.lastSelOp = opId;\n if (options && options.clearRedo !== false)\n { clearSelectionEvents(hist.undone); }\n }\n\n function pushSelectionToHistory(sel, dest) {\n var top = lst(dest);\n if (!(top && top.ranges && top.equals(sel)))\n { dest.push(sel); }\n }\n\n // Used to store marked span information in the history.\n function attachLocalSpans(doc, change, from, to) {\n var existing = change[\"spans_\" + doc.id], n = 0;\n doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {\n if (line.markedSpans)\n { (existing || (existing = change[\"spans_\" + doc.id] = {}))[n] = line.markedSpans; }\n ++n;\n });\n }\n\n // When un/re-doing restores text containing marked spans, those\n // that have been explicitly cleared should not be restored.\n function removeClearedSpans(spans) {\n if (!spans) { return null }\n var out;\n for (var i = 0; i < spans.length; ++i) {\n if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } }\n else if (out) { out.push(spans[i]); }\n }\n return !out ? spans : out.length ? out : null\n }\n\n // Retrieve and filter the old marked spans stored in a change event.\n function getOldSpans(doc, change) {\n var found = change[\"spans_\" + doc.id];\n if (!found) { return null }\n var nw = [];\n for (var i = 0; i < change.text.length; ++i)\n { nw.push(removeClearedSpans(found[i])); }\n return nw\n }\n\n // Used for un/re-doing changes from the history. Combines the\n // result of computing the existing spans with the set of spans that\n // existed in the history (so that deleting around a span and then\n // undoing brings back the span).\n function mergeOldSpans(doc, change) {\n var old = getOldSpans(doc, change);\n var stretched = stretchSpansOverChange(doc, change);\n if (!old) { return stretched }\n if (!stretched) { return old }\n\n for (var i = 0; i < old.length; ++i) {\n var oldCur = old[i], stretchCur = stretched[i];\n if (oldCur && stretchCur) {\n spans: for (var j = 0; j < stretchCur.length; ++j) {\n var span = stretchCur[j];\n for (var k = 0; k < oldCur.length; ++k)\n { if (oldCur[k].marker == span.marker) { continue spans } }\n oldCur.push(span);\n }\n } else if (stretchCur) {\n old[i] = stretchCur;\n }\n }\n return old\n }\n\n // Used both to provide a JSON-safe object in .getHistory, and, when\n // detaching a document, to split the history in two\n function copyHistoryArray(events, newGroup, instantiateSel) {\n var copy = [];\n for (var i = 0; i < events.length; ++i) {\n var event = events[i];\n if (event.ranges) {\n copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);\n continue\n }\n var changes = event.changes, newChanges = [];\n copy.push({changes: newChanges});\n for (var j = 0; j < changes.length; ++j) {\n var change = changes[j], m = (void 0);\n newChanges.push({from: change.from, to: change.to, text: change.text});\n if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\\d+)$/)) {\n if (indexOf(newGroup, Number(m[1])) > -1) {\n lst(newChanges)[prop] = change[prop];\n delete change[prop];\n }\n } } }\n }\n }\n return copy\n }\n\n // The 'scroll' parameter given to many of these indicated whether\n // the new cursor position should be scrolled into view after\n // modifying the selection.\n\n // If shift is held or the extend flag is set, extends a range to\n // include a given position (and optionally a second position).\n // Otherwise, simply returns the range between the given positions.\n // Used for cursor motion and such.\n function extendRange(range, head, other, extend) {\n if (extend) {\n var anchor = range.anchor;\n if (other) {\n var posBefore = cmp(head, anchor) < 0;\n if (posBefore != (cmp(other, anchor) < 0)) {\n anchor = head;\n head = other;\n } else if (posBefore != (cmp(head, other) < 0)) {\n head = other;\n }\n }\n return new Range(anchor, head)\n } else {\n return new Range(other || head, head)\n }\n }\n\n // Extend the primary selection range, discard the rest.\n function extendSelection(doc, head, other, options, extend) {\n if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); }\n setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options);\n }\n\n // Extend all selections (pos is an array of selections with length\n // equal the number of selections)\n function extendSelections(doc, heads, options) {\n var out = [];\n var extend = doc.cm && (doc.cm.display.shift || doc.extend);\n for (var i = 0; i < doc.sel.ranges.length; i++)\n { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); }\n var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex);\n setSelection(doc, newSel, options);\n }\n\n // Updates a single range in the selection.\n function replaceOneSelection(doc, i, range, options) {\n var ranges = doc.sel.ranges.slice(0);\n ranges[i] = range;\n setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options);\n }\n\n // Reset the selection to a single range.\n function setSimpleSelection(doc, anchor, head, options) {\n setSelection(doc, simpleSelection(anchor, head), options);\n }\n\n // Give beforeSelectionChange handlers a change to influence a\n // selection update.\n function filterSelectionChange(doc, sel, options) {\n var obj = {\n ranges: sel.ranges,\n update: function(ranges) {\n this.ranges = [];\n for (var i = 0; i < ranges.length; i++)\n { this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),\n clipPos(doc, ranges[i].head)); }\n },\n origin: options && options.origin\n };\n signal(doc, \"beforeSelectionChange\", doc, obj);\n if (doc.cm) { signal(doc.cm, \"beforeSelectionChange\", doc.cm, obj); }\n if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) }\n else { return sel }\n }\n\n function setSelectionReplaceHistory(doc, sel, options) {\n var done = doc.history.done, last = lst(done);\n if (last && last.ranges) {\n done[done.length - 1] = sel;\n setSelectionNoUndo(doc, sel, options);\n } else {\n setSelection(doc, sel, options);\n }\n }\n\n // Set a new selection.\n function setSelection(doc, sel, options) {\n setSelectionNoUndo(doc, sel, options);\n addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);\n }\n\n function setSelectionNoUndo(doc, sel, options) {\n if (hasHandler(doc, \"beforeSelectionChange\") || doc.cm && hasHandler(doc.cm, \"beforeSelectionChange\"))\n { sel = filterSelectionChange(doc, sel, options); }\n\n var bias = options && options.bias ||\n (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);\n setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));\n\n if (!(options && options.scroll === false) && doc.cm)\n { ensureCursorVisible(doc.cm); }\n }\n\n function setSelectionInner(doc, sel) {\n if (sel.equals(doc.sel)) { return }\n\n doc.sel = sel;\n\n if (doc.cm) {\n doc.cm.curOp.updateInput = 1;\n doc.cm.curOp.selectionChanged = true;\n signalCursorActivity(doc.cm);\n }\n signalLater(doc, \"cursorActivity\", doc);\n }\n\n // Verify that the selection does not partially select any atomic\n // marked ranges.\n function reCheckSelection(doc) {\n setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false));\n }\n\n // Return a selection that does not partially select any atomic\n // ranges.\n function skipAtomicInSelection(doc, sel, bias, mayClear) {\n var out;\n for (var i = 0; i < sel.ranges.length; i++) {\n var range = sel.ranges[i];\n var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];\n var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);\n var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);\n if (out || newAnchor != range.anchor || newHead != range.head) {\n if (!out) { out = sel.ranges.slice(0, i); }\n out[i] = new Range(newAnchor, newHead);\n }\n }\n return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel\n }\n\n function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {\n var line = getLine(doc, pos.line);\n if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {\n var sp = line.markedSpans[i], m = sp.marker;\n\n // Determine if we should prevent the cursor being placed to the left/right of an atomic marker\n // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it\n // is with selectLeft/Right\n var preventCursorLeft = (\"selectLeft\" in m) ? !m.selectLeft : m.inclusiveLeft;\n var preventCursorRight = (\"selectRight\" in m) ? !m.selectRight : m.inclusiveRight;\n\n if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&\n (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) {\n if (mayClear) {\n signal(m, \"beforeCursorEnter\");\n if (m.explicitlyCleared) {\n if (!line.markedSpans) { break }\n else {--i; continue}\n }\n }\n if (!m.atomic) { continue }\n\n if (oldPos) {\n var near = m.find(dir < 0 ? 1 : -1), diff = (void 0);\n if (dir < 0 ? preventCursorRight : preventCursorLeft)\n { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); }\n if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))\n { return skipAtomicInner(doc, near, pos, dir, mayClear) }\n }\n\n var far = m.find(dir < 0 ? -1 : 1);\n if (dir < 0 ? preventCursorLeft : preventCursorRight)\n { far = movePos(doc, far, dir, far.line == pos.line ? line : null); }\n return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null\n }\n } }\n return pos\n }\n\n // Ensure a given position is not inside an atomic range.\n function skipAtomic(doc, pos, oldPos, bias, mayClear) {\n var dir = bias || 1;\n var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||\n (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||\n skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||\n (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));\n if (!found) {\n doc.cantEdit = true;\n return Pos(doc.first, 0)\n }\n return found\n }\n\n function movePos(doc, pos, dir, line) {\n if (dir < 0 && pos.ch == 0) {\n if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }\n else { return null }\n } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {\n if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }\n else { return null }\n } else {\n return new Pos(pos.line, pos.ch + dir)\n }\n }\n\n function selectAll(cm) {\n cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);\n }\n\n // UPDATING\n\n // Allow \"beforeChange\" event handlers to influence a change\n function filterChange(doc, change, update) {\n var obj = {\n canceled: false,\n from: change.from,\n to: change.to,\n text: change.text,\n origin: change.origin,\n cancel: function () { return obj.canceled = true; }\n };\n if (update) { obj.update = function (from, to, text, origin) {\n if (from) { obj.from = clipPos(doc, from); }\n if (to) { obj.to = clipPos(doc, to); }\n if (text) { obj.text = text; }\n if (origin !== undefined) { obj.origin = origin; }\n }; }\n signal(doc, \"beforeChange\", doc, obj);\n if (doc.cm) { signal(doc.cm, \"beforeChange\", doc.cm, obj); }\n\n if (obj.canceled) {\n if (doc.cm) { doc.cm.curOp.updateInput = 2; }\n return null\n }\n return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}\n }\n\n // Apply a change to a document, and add it to the document's\n // history, and propagating it to all linked documents.\n function makeChange(doc, change, ignoreReadOnly) {\n if (doc.cm) {\n if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }\n if (doc.cm.state.suppressEdits) { return }\n }\n\n if (hasHandler(doc, \"beforeChange\") || doc.cm && hasHandler(doc.cm, \"beforeChange\")) {\n change = filterChange(doc, change, true);\n if (!change) { return }\n }\n\n // Possibly split or suppress the update based on the presence\n // of read-only spans in its range.\n var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);\n if (split) {\n for (var i = split.length - 1; i >= 0; --i)\n { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [\"\"] : change.text, origin: change.origin}); }\n } else {\n makeChangeInner(doc, change);\n }\n }\n\n function makeChangeInner(doc, change) {\n if (change.text.length == 1 && change.text[0] == \"\" && cmp(change.from, change.to) == 0) { return }\n var selAfter = computeSelAfterChange(doc, change);\n addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);\n\n makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));\n var rebased = [];\n\n linkedDocs(doc, function (doc, sharedHist) {\n if (!sharedHist && indexOf(rebased, doc.history) == -1) {\n rebaseHist(doc.history, change);\n rebased.push(doc.history);\n }\n makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));\n });\n }\n\n // Revert a change stored in a document's history.\n function makeChangeFromHistory(doc, type, allowSelectionOnly) {\n var suppress = doc.cm && doc.cm.state.suppressEdits;\n if (suppress && !allowSelectionOnly) { return }\n\n var hist = doc.history, event, selAfter = doc.sel;\n var source = type == \"undo\" ? hist.done : hist.undone, dest = type == \"undo\" ? hist.undone : hist.done;\n\n // Verify that there is a useable event (so that ctrl-z won't\n // needlessly clear selection events)\n var i = 0;\n for (; i < source.length; i++) {\n event = source[i];\n if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)\n { break }\n }\n if (i == source.length) { return }\n hist.lastOrigin = hist.lastSelOrigin = null;\n\n for (;;) {\n event = source.pop();\n if (event.ranges) {\n pushSelectionToHistory(event, dest);\n if (allowSelectionOnly && !event.equals(doc.sel)) {\n setSelection(doc, event, {clearRedo: false});\n return\n }\n selAfter = event;\n } else if (suppress) {\n source.push(event);\n return\n } else { break }\n }\n\n // Build up a reverse change object to add to the opposite history\n // stack (redo when undoing, and vice versa).\n var antiChanges = [];\n pushSelectionToHistory(selAfter, dest);\n dest.push({changes: antiChanges, generation: hist.generation});\n hist.generation = event.generation || ++hist.maxGeneration;\n\n var filter = hasHandler(doc, \"beforeChange\") || doc.cm && hasHandler(doc.cm, \"beforeChange\");\n\n var loop = function ( i ) {\n var change = event.changes[i];\n change.origin = type;\n if (filter && !filterChange(doc, change, false)) {\n source.length = 0;\n return {}\n }\n\n antiChanges.push(historyChangeFromChange(doc, change));\n\n var after = i ? computeSelAfterChange(doc, change) : lst(source);\n makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));\n if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); }\n var rebased = [];\n\n // Propagate to the linked documents\n linkedDocs(doc, function (doc, sharedHist) {\n if (!sharedHist && indexOf(rebased, doc.history) == -1) {\n rebaseHist(doc.history, change);\n rebased.push(doc.history);\n }\n makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));\n });\n };\n\n for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {\n var returned = loop( i$1 );\n\n if ( returned ) return returned.v;\n }\n }\n\n // Sub-views need their line numbers shifted when text is added\n // above or below them in the parent document.\n function shiftDoc(doc, distance) {\n if (distance == 0) { return }\n doc.first += distance;\n doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(\n Pos(range.anchor.line + distance, range.anchor.ch),\n Pos(range.head.line + distance, range.head.ch)\n ); }), doc.sel.primIndex);\n if (doc.cm) {\n regChange(doc.cm, doc.first, doc.first - distance, distance);\n for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)\n { regLineChange(doc.cm, l, \"gutter\"); }\n }\n }\n\n // More lower-level change function, handling only a single document\n // (not linked ones).\n function makeChangeSingleDoc(doc, change, selAfter, spans) {\n if (doc.cm && !doc.cm.curOp)\n { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }\n\n if (change.to.line < doc.first) {\n shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));\n return\n }\n if (change.from.line > doc.lastLine()) { return }\n\n // Clip the change to the size of this doc\n if (change.from.line < doc.first) {\n var shift = change.text.length - 1 - (doc.first - change.from.line);\n shiftDoc(doc, shift);\n change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),\n text: [lst(change.text)], origin: change.origin};\n }\n var last = doc.lastLine();\n if (change.to.line > last) {\n change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),\n text: [change.text[0]], origin: change.origin};\n }\n\n change.removed = getBetween(doc, change.from, change.to);\n\n if (!selAfter) { selAfter = computeSelAfterChange(doc, change); }\n if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); }\n else { updateDoc(doc, change, spans); }\n setSelectionNoUndo(doc, selAfter, sel_dontScroll);\n\n if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0)))\n { doc.cantEdit = false; }\n }\n\n // Handle the interaction of a change to a document with the editor\n // that this document is part of.\n function makeChangeSingleDocInEditor(cm, change, spans) {\n var doc = cm.doc, display = cm.display, from = change.from, to = change.to;\n\n var recomputeMaxLength = false, checkWidthStart = from.line;\n if (!cm.options.lineWrapping) {\n checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));\n doc.iter(checkWidthStart, to.line + 1, function (line) {\n if (line == display.maxLine) {\n recomputeMaxLength = true;\n return true\n }\n });\n }\n\n if (doc.sel.contains(change.from, change.to) > -1)\n { signalCursorActivity(cm); }\n\n updateDoc(doc, change, spans, estimateHeight(cm));\n\n if (!cm.options.lineWrapping) {\n doc.iter(checkWidthStart, from.line + change.text.length, function (line) {\n var len = lineLength(line);\n if (len > display.maxLineLength) {\n display.maxLine = line;\n display.maxLineLength = len;\n display.maxLineChanged = true;\n recomputeMaxLength = false;\n }\n });\n if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; }\n }\n\n retreatFrontier(doc, from.line);\n startWorker(cm, 400);\n\n var lendiff = change.text.length - (to.line - from.line) - 1;\n // Remember that these lines changed, for updating the display\n if (change.full)\n { regChange(cm); }\n else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))\n { regLineChange(cm, from.line, \"text\"); }\n else\n { regChange(cm, from.line, to.line + 1, lendiff); }\n\n var changesHandler = hasHandler(cm, \"changes\"), changeHandler = hasHandler(cm, \"change\");\n if (changeHandler || changesHandler) {\n var obj = {\n from: from, to: to,\n text: change.text,\n removed: change.removed,\n origin: change.origin\n };\n if (changeHandler) { signalLater(cm, \"change\", cm, obj); }\n if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); }\n }\n cm.display.selForContextMenu = null;\n }\n\n function replaceRange(doc, code, from, to, origin) {\n var assign;\n\n if (!to) { to = from; }\n if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); }\n if (typeof code == \"string\") { code = doc.splitLines(code); }\n makeChange(doc, {from: from, to: to, text: code, origin: origin});\n }\n\n // Rebasing/resetting history to deal with externally-sourced changes\n\n function rebaseHistSelSingle(pos, from, to, diff) {\n if (to < pos.line) {\n pos.line += diff;\n } else if (from < pos.line) {\n pos.line = from;\n pos.ch = 0;\n }\n }\n\n // Tries to rebase an array of history events given a change in the\n // document. If the change touches the same lines as the event, the\n // event, and everything 'behind' it, is discarded. If the change is\n // before the event, the event's positions are updated. Uses a\n // copy-on-write scheme for the positions, to avoid having to\n // reallocate them all on every rebase, but also avoid problems with\n // shared position objects being unsafely updated.\n function rebaseHistArray(array, from, to, diff) {\n for (var i = 0; i < array.length; ++i) {\n var sub = array[i], ok = true;\n if (sub.ranges) {\n if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }\n for (var j = 0; j < sub.ranges.length; j++) {\n rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);\n rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);\n }\n continue\n }\n for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {\n var cur = sub.changes[j$1];\n if (to < cur.from.line) {\n cur.from = Pos(cur.from.line + diff, cur.from.ch);\n cur.to = Pos(cur.to.line + diff, cur.to.ch);\n } else if (from <= cur.to.line) {\n ok = false;\n break\n }\n }\n if (!ok) {\n array.splice(0, i + 1);\n i = 0;\n }\n }\n }\n\n function rebaseHist(hist, change) {\n var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;\n rebaseHistArray(hist.done, from, to, diff);\n rebaseHistArray(hist.undone, from, to, diff);\n }\n\n // Utility for applying a change to a line by handle or number,\n // returning the number and optionally registering the line as\n // changed.\n function changeLine(doc, handle, changeType, op) {\n var no = handle, line = handle;\n if (typeof handle == \"number\") { line = getLine(doc, clipLine(doc, handle)); }\n else { no = lineNo(handle); }\n if (no == null) { return null }\n if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); }\n return line\n }\n\n // The document is represented as a BTree consisting of leaves, with\n // chunk of lines in them, and branches, with up to ten leaves or\n // other branch nodes below them. The top node is always a branch\n // node, and is the document object itself (meaning it has\n // additional methods and properties).\n //\n // All nodes have parent links. The tree is used both to go from\n // line numbers to line objects, and to go from objects to numbers.\n // It also indexes by height, and is used to convert between height\n // and line object, and to find the total height of the document.\n //\n // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html\n\n function LeafChunk(lines) {\n this.lines = lines;\n this.parent = null;\n var height = 0;\n for (var i = 0; i < lines.length; ++i) {\n lines[i].parent = this;\n height += lines[i].height;\n }\n this.height = height;\n }\n\n LeafChunk.prototype = {\n chunkSize: function() { return this.lines.length },\n\n // Remove the n lines at offset 'at'.\n removeInner: function(at, n) {\n for (var i = at, e = at + n; i < e; ++i) {\n var line = this.lines[i];\n this.height -= line.height;\n cleanUpLine(line);\n signalLater(line, \"delete\");\n }\n this.lines.splice(at, n);\n },\n\n // Helper used to collapse a small branch into a single leaf.\n collapse: function(lines) {\n lines.push.apply(lines, this.lines);\n },\n\n // Insert the given array of lines at offset 'at', count them as\n // having the given height.\n insertInner: function(at, lines, height) {\n this.height += height;\n this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));\n for (var i = 0; i < lines.length; ++i) { lines[i].parent = this; }\n },\n\n // Used to iterate over a part of the tree.\n iterN: function(at, n, op) {\n for (var e = at + n; at < e; ++at)\n { if (op(this.lines[at])) { return true } }\n }\n };\n\n function BranchChunk(children) {\n this.children = children;\n var size = 0, height = 0;\n for (var i = 0; i < children.length; ++i) {\n var ch = children[i];\n size += ch.chunkSize(); height += ch.height;\n ch.parent = this;\n }\n this.size = size;\n this.height = height;\n this.parent = null;\n }\n\n BranchChunk.prototype = {\n chunkSize: function() { return this.size },\n\n removeInner: function(at, n) {\n this.size -= n;\n for (var i = 0; i < this.children.length; ++i) {\n var child = this.children[i], sz = child.chunkSize();\n if (at < sz) {\n var rm = Math.min(n, sz - at), oldHeight = child.height;\n child.removeInner(at, rm);\n this.height -= oldHeight - child.height;\n if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }\n if ((n -= rm) == 0) { break }\n at = 0;\n } else { at -= sz; }\n }\n // If the result is smaller than 25 lines, ensure that it is a\n // single leaf node.\n if (this.size - n < 25 &&\n (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {\n var lines = [];\n this.collapse(lines);\n this.children = [new LeafChunk(lines)];\n this.children[0].parent = this;\n }\n },\n\n collapse: function(lines) {\n for (var i = 0; i < this.children.length; ++i) { this.children[i].collapse(lines); }\n },\n\n insertInner: function(at, lines, height) {\n this.size += lines.length;\n this.height += height;\n for (var i = 0; i < this.children.length; ++i) {\n var child = this.children[i], sz = child.chunkSize();\n if (at <= sz) {\n child.insertInner(at, lines, height);\n if (child.lines && child.lines.length > 50) {\n // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.\n // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.\n var remaining = child.lines.length % 25 + 25;\n for (var pos = remaining; pos < child.lines.length;) {\n var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));\n child.height -= leaf.height;\n this.children.splice(++i, 0, leaf);\n leaf.parent = this;\n }\n child.lines = child.lines.slice(0, remaining);\n this.maybeSpill();\n }\n break\n }\n at -= sz;\n }\n },\n\n // When a node has grown, check whether it should be split.\n maybeSpill: function() {\n if (this.children.length <= 10) { return }\n var me = this;\n do {\n var spilled = me.children.splice(me.children.length - 5, 5);\n var sibling = new BranchChunk(spilled);\n if (!me.parent) { // Become the parent node\n var copy = new BranchChunk(me.children);\n copy.parent = me;\n me.children = [copy, sibling];\n me = copy;\n } else {\n me.size -= sibling.size;\n me.height -= sibling.height;\n var myIndex = indexOf(me.parent.children, me);\n me.parent.children.splice(myIndex + 1, 0, sibling);\n }\n sibling.parent = me.parent;\n } while (me.children.length > 10)\n me.parent.maybeSpill();\n },\n\n iterN: function(at, n, op) {\n for (var i = 0; i < this.children.length; ++i) {\n var child = this.children[i], sz = child.chunkSize();\n if (at < sz) {\n var used = Math.min(n, sz - at);\n if (child.iterN(at, used, op)) { return true }\n if ((n -= used) == 0) { break }\n at = 0;\n } else { at -= sz; }\n }\n }\n };\n\n // Line widgets are block elements displayed above or below a line.\n\n var LineWidget = function(doc, node, options) {\n if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))\n { this[opt] = options[opt]; } } }\n this.doc = doc;\n this.node = node;\n };\n\n LineWidget.prototype.clear = function () {\n var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);\n if (no == null || !ws) { return }\n for (var i = 0; i < ws.length; ++i) { if (ws[i] == this) { ws.splice(i--, 1); } }\n if (!ws.length) { line.widgets = null; }\n var height = widgetHeight(this);\n updateLineHeight(line, Math.max(0, line.height - height));\n if (cm) {\n runInOp(cm, function () {\n adjustScrollWhenAboveVisible(cm, line, -height);\n regLineChange(cm, no, \"widget\");\n });\n signalLater(cm, \"lineWidgetCleared\", cm, this, no);\n }\n };\n\n LineWidget.prototype.changed = function () {\n var this$1 = this;\n\n var oldH = this.height, cm = this.doc.cm, line = this.line;\n this.height = null;\n var diff = widgetHeight(this) - oldH;\n if (!diff) { return }\n if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); }\n if (cm) {\n runInOp(cm, function () {\n cm.curOp.forceUpdate = true;\n adjustScrollWhenAboveVisible(cm, line, diff);\n signalLater(cm, \"lineWidgetChanged\", cm, this$1, lineNo(line));\n });\n }\n };\n eventMixin(LineWidget);\n\n function adjustScrollWhenAboveVisible(cm, line, diff) {\n if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))\n { addToScrollTop(cm, diff); }\n }\n\n function addLineWidget(doc, handle, node, options) {\n var widget = new LineWidget(doc, node, options);\n var cm = doc.cm;\n if (cm && widget.noHScroll) { cm.display.alignWidgets = true; }\n changeLine(doc, handle, \"widget\", function (line) {\n var widgets = line.widgets || (line.widgets = []);\n if (widget.insertAt == null) { widgets.push(widget); }\n else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); }\n widget.line = line;\n if (cm && !lineIsHidden(doc, line)) {\n var aboveVisible = heightAtLine(line) < doc.scrollTop;\n updateLineHeight(line, line.height + widgetHeight(widget));\n if (aboveVisible) { addToScrollTop(cm, widget.height); }\n cm.curOp.forceUpdate = true;\n }\n return true\n });\n if (cm) { signalLater(cm, \"lineWidgetAdded\", cm, widget, typeof handle == \"number\" ? handle : lineNo(handle)); }\n return widget\n }\n\n // TEXTMARKERS\n\n // Created with markText and setBookmark methods. A TextMarker is a\n // handle that can be used to clear or find a marked position in the\n // document. Line objects hold arrays (markedSpans) containing\n // {from, to, marker} object pointing to such marker objects, and\n // indicating that such a marker is present on that line. Multiple\n // lines may point to the same marker when it spans across lines.\n // The spans will have null for their from/to properties when the\n // marker continues beyond the start/end of the line. Markers have\n // links back to the lines they currently touch.\n\n // Collapsed markers have unique ids, in order to be able to order\n // them, which is needed for uniquely determining an outer marker\n // when they overlap (they may nest, but not partially overlap).\n var nextMarkerId = 0;\n\n var TextMarker = function(doc, type) {\n this.lines = [];\n this.type = type;\n this.doc = doc;\n this.id = ++nextMarkerId;\n };\n\n // Clear the marker.\n TextMarker.prototype.clear = function () {\n if (this.explicitlyCleared) { return }\n var cm = this.doc.cm, withOp = cm && !cm.curOp;\n if (withOp) { startOperation(cm); }\n if (hasHandler(this, \"clear\")) {\n var found = this.find();\n if (found) { signalLater(this, \"clear\", found.from, found.to); }\n }\n var min = null, max = null;\n for (var i = 0; i < this.lines.length; ++i) {\n var line = this.lines[i];\n var span = getMarkedSpanFor(line.markedSpans, this);\n if (cm && !this.collapsed) { regLineChange(cm, lineNo(line), \"text\"); }\n else if (cm) {\n if (span.to != null) { max = lineNo(line); }\n if (span.from != null) { min = lineNo(line); }\n }\n line.markedSpans = removeMarkedSpan(line.markedSpans, span);\n if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm)\n { updateLineHeight(line, textHeight(cm.display)); }\n }\n if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {\n var visual = visualLine(this.lines[i$1]), len = lineLength(visual);\n if (len > cm.display.maxLineLength) {\n cm.display.maxLine = visual;\n cm.display.maxLineLength = len;\n cm.display.maxLineChanged = true;\n }\n } }\n\n if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); }\n this.lines.length = 0;\n this.explicitlyCleared = true;\n if (this.atomic && this.doc.cantEdit) {\n this.doc.cantEdit = false;\n if (cm) { reCheckSelection(cm.doc); }\n }\n if (cm) { signalLater(cm, \"markerCleared\", cm, this, min, max); }\n if (withOp) { endOperation(cm); }\n if (this.parent) { this.parent.clear(); }\n };\n\n // Find the position of the marker in the document. Returns a {from,\n // to} object by default. Side can be passed to get a specific side\n // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the\n // Pos objects returned contain a line object, rather than a line\n // number (used to prevent looking up the same line twice).\n TextMarker.prototype.find = function (side, lineObj) {\n if (side == null && this.type == \"bookmark\") { side = 1; }\n var from, to;\n for (var i = 0; i < this.lines.length; ++i) {\n var line = this.lines[i];\n var span = getMarkedSpanFor(line.markedSpans, this);\n if (span.from != null) {\n from = Pos(lineObj ? line : lineNo(line), span.from);\n if (side == -1) { return from }\n }\n if (span.to != null) {\n to = Pos(lineObj ? line : lineNo(line), span.to);\n if (side == 1) { return to }\n }\n }\n return from && {from: from, to: to}\n };\n\n // Signals that the marker's widget changed, and surrounding layout\n // should be recomputed.\n TextMarker.prototype.changed = function () {\n var this$1 = this;\n\n var pos = this.find(-1, true), widget = this, cm = this.doc.cm;\n if (!pos || !cm) { return }\n runInOp(cm, function () {\n var line = pos.line, lineN = lineNo(pos.line);\n var view = findViewForLine(cm, lineN);\n if (view) {\n clearLineMeasurementCacheFor(view);\n cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;\n }\n cm.curOp.updateMaxLine = true;\n if (!lineIsHidden(widget.doc, line) && widget.height != null) {\n var oldHeight = widget.height;\n widget.height = null;\n var dHeight = widgetHeight(widget) - oldHeight;\n if (dHeight)\n { updateLineHeight(line, line.height + dHeight); }\n }\n signalLater(cm, \"markerChanged\", cm, this$1);\n });\n };\n\n TextMarker.prototype.attachLine = function (line) {\n if (!this.lines.length && this.doc.cm) {\n var op = this.doc.cm.curOp;\n if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)\n { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); }\n }\n this.lines.push(line);\n };\n\n TextMarker.prototype.detachLine = function (line) {\n this.lines.splice(indexOf(this.lines, line), 1);\n if (!this.lines.length && this.doc.cm) {\n var op = this.doc.cm.curOp\n ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);\n }\n };\n eventMixin(TextMarker);\n\n // Create a marker, wire it up to the right lines, and\n function markText(doc, from, to, options, type) {\n // Shared markers (across linked documents) are handled separately\n // (markTextShared will call out to this again, once per\n // document).\n if (options && options.shared) { return markTextShared(doc, from, to, options, type) }\n // Ensure we are in an operation.\n if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }\n\n var marker = new TextMarker(doc, type), diff = cmp(from, to);\n if (options) { copyObj(options, marker, false); }\n // Don't connect empty markers unless clearWhenEmpty is false\n if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)\n { return marker }\n if (marker.replacedWith) {\n // Showing up as a widget implies collapsed (widget replaces text)\n marker.collapsed = true;\n marker.widgetNode = eltP(\"span\", [marker.replacedWith], \"CodeMirror-widget\");\n if (!options.handleMouseEvents) { marker.widgetNode.setAttribute(\"cm-ignore-events\", \"true\"); }\n if (options.insertLeft) { marker.widgetNode.insertLeft = true; }\n }\n if (marker.collapsed) {\n if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||\n from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))\n { throw new Error(\"Inserting collapsed marker partially overlapping an existing one\") }\n seeCollapsedSpans();\n }\n\n if (marker.addToHistory)\n { addChangeToHistory(doc, {from: from, to: to, origin: \"markText\"}, doc.sel, NaN); }\n\n var curLine = from.line, cm = doc.cm, updateMaxLine;\n doc.iter(curLine, to.line + 1, function (line) {\n if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)\n { updateMaxLine = true; }\n if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); }\n addMarkedSpan(line, new MarkedSpan(marker,\n curLine == from.line ? from.ch : null,\n curLine == to.line ? to.ch : null));\n ++curLine;\n });\n // lineIsHidden depends on the presence of the spans, so needs a second pass\n if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {\n if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); }\n }); }\n\n if (marker.clearOnEnter) { on(marker, \"beforeCursorEnter\", function () { return marker.clear(); }); }\n\n if (marker.readOnly) {\n seeReadOnlySpans();\n if (doc.history.done.length || doc.history.undone.length)\n { doc.clearHistory(); }\n }\n if (marker.collapsed) {\n marker.id = ++nextMarkerId;\n marker.atomic = true;\n }\n if (cm) {\n // Sync editor state\n if (updateMaxLine) { cm.curOp.updateMaxLine = true; }\n if (marker.collapsed)\n { regChange(cm, from.line, to.line + 1); }\n else if (marker.className || marker.startStyle || marker.endStyle || marker.css ||\n marker.attributes || marker.title)\n { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, \"text\"); } }\n if (marker.atomic) { reCheckSelection(cm.doc); }\n signalLater(cm, \"markerAdded\", cm, marker);\n }\n return marker\n }\n\n // SHARED TEXTMARKERS\n\n // A shared marker spans multiple linked documents. It is\n // implemented as a meta-marker-object controlling multiple normal\n // markers.\n var SharedTextMarker = function(markers, primary) {\n this.markers = markers;\n this.primary = primary;\n for (var i = 0; i < markers.length; ++i)\n { markers[i].parent = this; }\n };\n\n SharedTextMarker.prototype.clear = function () {\n if (this.explicitlyCleared) { return }\n this.explicitlyCleared = true;\n for (var i = 0; i < this.markers.length; ++i)\n { this.markers[i].clear(); }\n signalLater(this, \"clear\");\n };\n\n SharedTextMarker.prototype.find = function (side, lineObj) {\n return this.primary.find(side, lineObj)\n };\n eventMixin(SharedTextMarker);\n\n function markTextShared(doc, from, to, options, type) {\n options = copyObj(options);\n options.shared = false;\n var markers = [markText(doc, from, to, options, type)], primary = markers[0];\n var widget = options.widgetNode;\n linkedDocs(doc, function (doc) {\n if (widget) { options.widgetNode = widget.cloneNode(true); }\n markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));\n for (var i = 0; i < doc.linked.length; ++i)\n { if (doc.linked[i].isParent) { return } }\n primary = lst(markers);\n });\n return new SharedTextMarker(markers, primary)\n }\n\n function findSharedMarkers(doc) {\n return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })\n }\n\n function copySharedMarkers(doc, markers) {\n for (var i = 0; i < markers.length; i++) {\n var marker = markers[i], pos = marker.find();\n var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);\n if (cmp(mFrom, mTo)) {\n var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);\n marker.markers.push(subMark);\n subMark.parent = marker;\n }\n }\n }\n\n function detachSharedMarkers(markers) {\n var loop = function ( i ) {\n var marker = markers[i], linked = [marker.primary.doc];\n linkedDocs(marker.primary.doc, function (d) { return linked.push(d); });\n for (var j = 0; j < marker.markers.length; j++) {\n var subMarker = marker.markers[j];\n if (indexOf(linked, subMarker.doc) == -1) {\n subMarker.parent = null;\n marker.markers.splice(j--, 1);\n }\n }\n };\n\n for (var i = 0; i < markers.length; i++) loop( i );\n }\n\n var nextDocId = 0;\n var Doc = function(text, mode, firstLine, lineSep, direction) {\n if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }\n if (firstLine == null) { firstLine = 0; }\n\n BranchChunk.call(this, [new LeafChunk([new Line(\"\", null)])]);\n this.first = firstLine;\n this.scrollTop = this.scrollLeft = 0;\n this.cantEdit = false;\n this.cleanGeneration = 1;\n this.modeFrontier = this.highlightFrontier = firstLine;\n var start = Pos(firstLine, 0);\n this.sel = simpleSelection(start);\n this.history = new History(null);\n this.id = ++nextDocId;\n this.modeOption = mode;\n this.lineSep = lineSep;\n this.direction = (direction == \"rtl\") ? \"rtl\" : \"ltr\";\n this.extend = false;\n\n if (typeof text == \"string\") { text = this.splitLines(text); }\n updateDoc(this, {from: start, to: start, text: text});\n setSelection(this, simpleSelection(start), sel_dontScroll);\n };\n\n Doc.prototype = createObj(BranchChunk.prototype, {\n constructor: Doc,\n // Iterate over the document. Supports two forms -- with only one\n // argument, it calls that for each line in the document. With\n // three, it iterates over the range given by the first two (with\n // the second being non-inclusive).\n iter: function(from, to, op) {\n if (op) { this.iterN(from - this.first, to - from, op); }\n else { this.iterN(this.first, this.first + this.size, from); }\n },\n\n // Non-public interface for adding and removing lines.\n insert: function(at, lines) {\n var height = 0;\n for (var i = 0; i < lines.length; ++i) { height += lines[i].height; }\n this.insertInner(at - this.first, lines, height);\n },\n remove: function(at, n) { this.removeInner(at - this.first, n); },\n\n // From here, the methods are part of the public interface. Most\n // are also available from CodeMirror (editor) instances.\n\n getValue: function(lineSep) {\n var lines = getLines(this, this.first, this.first + this.size);\n if (lineSep === false) { return lines }\n return lines.join(lineSep || this.lineSeparator())\n },\n setValue: docMethodOp(function(code) {\n var top = Pos(this.first, 0), last = this.first + this.size - 1;\n makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),\n text: this.splitLines(code), origin: \"setValue\", full: true}, true);\n if (this.cm) { scrollToCoords(this.cm, 0, 0); }\n setSelection(this, simpleSelection(top), sel_dontScroll);\n }),\n replaceRange: function(code, from, to, origin) {\n from = clipPos(this, from);\n to = to ? clipPos(this, to) : from;\n replaceRange(this, code, from, to, origin);\n },\n getRange: function(from, to, lineSep) {\n var lines = getBetween(this, clipPos(this, from), clipPos(this, to));\n if (lineSep === false) { return lines }\n return lines.join(lineSep || this.lineSeparator())\n },\n\n getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},\n\n getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},\n getLineNumber: function(line) {return lineNo(line)},\n\n getLineHandleVisualStart: function(line) {\n if (typeof line == \"number\") { line = getLine(this, line); }\n return visualLine(line)\n },\n\n lineCount: function() {return this.size},\n firstLine: function() {return this.first},\n lastLine: function() {return this.first + this.size - 1},\n\n clipPos: function(pos) {return clipPos(this, pos)},\n\n getCursor: function(start) {\n var range = this.sel.primary(), pos;\n if (start == null || start == \"head\") { pos = range.head; }\n else if (start == \"anchor\") { pos = range.anchor; }\n else if (start == \"end\" || start == \"to\" || start === false) { pos = range.to(); }\n else { pos = range.from(); }\n return pos\n },\n listSelections: function() { return this.sel.ranges },\n somethingSelected: function() {return this.sel.somethingSelected()},\n\n setCursor: docMethodOp(function(line, ch, options) {\n setSimpleSelection(this, clipPos(this, typeof line == \"number\" ? Pos(line, ch || 0) : line), null, options);\n }),\n setSelection: docMethodOp(function(anchor, head, options) {\n setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);\n }),\n extendSelection: docMethodOp(function(head, other, options) {\n extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);\n }),\n extendSelections: docMethodOp(function(heads, options) {\n extendSelections(this, clipPosArray(this, heads), options);\n }),\n extendSelectionsBy: docMethodOp(function(f, options) {\n var heads = map(this.sel.ranges, f);\n extendSelections(this, clipPosArray(this, heads), options);\n }),\n setSelections: docMethodOp(function(ranges, primary, options) {\n if (!ranges.length) { return }\n var out = [];\n for (var i = 0; i < ranges.length; i++)\n { out[i] = new Range(clipPos(this, ranges[i].anchor),\n clipPos(this, ranges[i].head)); }\n if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); }\n setSelection(this, normalizeSelection(this.cm, out, primary), options);\n }),\n addSelection: docMethodOp(function(anchor, head, options) {\n var ranges = this.sel.ranges.slice(0);\n ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));\n setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options);\n }),\n\n getSelection: function(lineSep) {\n var ranges = this.sel.ranges, lines;\n for (var i = 0; i < ranges.length; i++) {\n var sel = getBetween(this, ranges[i].from(), ranges[i].to());\n lines = lines ? lines.concat(sel) : sel;\n }\n if (lineSep === false) { return lines }\n else { return lines.join(lineSep || this.lineSeparator()) }\n },\n getSelections: function(lineSep) {\n var parts = [], ranges = this.sel.ranges;\n for (var i = 0; i < ranges.length; i++) {\n var sel = getBetween(this, ranges[i].from(), ranges[i].to());\n if (lineSep !== false) { sel = sel.join(lineSep || this.lineSeparator()); }\n parts[i] = sel;\n }\n return parts\n },\n replaceSelection: function(code, collapse, origin) {\n var dup = [];\n for (var i = 0; i < this.sel.ranges.length; i++)\n { dup[i] = code; }\n this.replaceSelections(dup, collapse, origin || \"+input\");\n },\n replaceSelections: docMethodOp(function(code, collapse, origin) {\n var changes = [], sel = this.sel;\n for (var i = 0; i < sel.ranges.length; i++) {\n var range = sel.ranges[i];\n changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin};\n }\n var newSel = collapse && collapse != \"end\" && computeReplacedSel(this, changes, collapse);\n for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)\n { makeChange(this, changes[i$1]); }\n if (newSel) { setSelectionReplaceHistory(this, newSel); }\n else if (this.cm) { ensureCursorVisible(this.cm); }\n }),\n undo: docMethodOp(function() {makeChangeFromHistory(this, \"undo\");}),\n redo: docMethodOp(function() {makeChangeFromHistory(this, \"redo\");}),\n undoSelection: docMethodOp(function() {makeChangeFromHistory(this, \"undo\", true);}),\n redoSelection: docMethodOp(function() {makeChangeFromHistory(this, \"redo\", true);}),\n\n setExtending: function(val) {this.extend = val;},\n getExtending: function() {return this.extend},\n\n historySize: function() {\n var hist = this.history, done = 0, undone = 0;\n for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } }\n for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } }\n return {undo: done, redo: undone}\n },\n clearHistory: function() {\n var this$1 = this;\n\n this.history = new History(this.history.maxGeneration);\n linkedDocs(this, function (doc) { return doc.history = this$1.history; }, true);\n },\n\n markClean: function() {\n this.cleanGeneration = this.changeGeneration(true);\n },\n changeGeneration: function(forceSplit) {\n if (forceSplit)\n { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; }\n return this.history.generation\n },\n isClean: function (gen) {\n return this.history.generation == (gen || this.cleanGeneration)\n },\n\n getHistory: function() {\n return {done: copyHistoryArray(this.history.done),\n undone: copyHistoryArray(this.history.undone)}\n },\n setHistory: function(histData) {\n var hist = this.history = new History(this.history.maxGeneration);\n hist.done = copyHistoryArray(histData.done.slice(0), null, true);\n hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);\n },\n\n setGutterMarker: docMethodOp(function(line, gutterID, value) {\n return changeLine(this, line, \"gutter\", function (line) {\n var markers = line.gutterMarkers || (line.gutterMarkers = {});\n markers[gutterID] = value;\n if (!value && isEmpty(markers)) { line.gutterMarkers = null; }\n return true\n })\n }),\n\n clearGutter: docMethodOp(function(gutterID) {\n var this$1 = this;\n\n this.iter(function (line) {\n if (line.gutterMarkers && line.gutterMarkers[gutterID]) {\n changeLine(this$1, line, \"gutter\", function () {\n line.gutterMarkers[gutterID] = null;\n if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; }\n return true\n });\n }\n });\n }),\n\n lineInfo: function(line) {\n var n;\n if (typeof line == \"number\") {\n if (!isLine(this, line)) { return null }\n n = line;\n line = getLine(this, line);\n if (!line) { return null }\n } else {\n n = lineNo(line);\n if (n == null) { return null }\n }\n return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,\n textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,\n widgets: line.widgets}\n },\n\n addLineClass: docMethodOp(function(handle, where, cls) {\n return changeLine(this, handle, where == \"gutter\" ? \"gutter\" : \"class\", function (line) {\n var prop = where == \"text\" ? \"textClass\"\n : where == \"background\" ? \"bgClass\"\n : where == \"gutter\" ? \"gutterClass\" : \"wrapClass\";\n if (!line[prop]) { line[prop] = cls; }\n else if (classTest(cls).test(line[prop])) { return false }\n else { line[prop] += \" \" + cls; }\n return true\n })\n }),\n removeLineClass: docMethodOp(function(handle, where, cls) {\n return changeLine(this, handle, where == \"gutter\" ? \"gutter\" : \"class\", function (line) {\n var prop = where == \"text\" ? \"textClass\"\n : where == \"background\" ? \"bgClass\"\n : where == \"gutter\" ? \"gutterClass\" : \"wrapClass\";\n var cur = line[prop];\n if (!cur) { return false }\n else if (cls == null) { line[prop] = null; }\n else {\n var found = cur.match(classTest(cls));\n if (!found) { return false }\n var end = found.index + found[0].length;\n line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? \"\" : \" \") + cur.slice(end) || null;\n }\n return true\n })\n }),\n\n addLineWidget: docMethodOp(function(handle, node, options) {\n return addLineWidget(this, handle, node, options)\n }),\n removeLineWidget: function(widget) { widget.clear(); },\n\n markText: function(from, to, options) {\n return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || \"range\")\n },\n setBookmark: function(pos, options) {\n var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),\n insertLeft: options && options.insertLeft,\n clearWhenEmpty: false, shared: options && options.shared,\n handleMouseEvents: options && options.handleMouseEvents};\n pos = clipPos(this, pos);\n return markText(this, pos, pos, realOpts, \"bookmark\")\n },\n findMarksAt: function(pos) {\n pos = clipPos(this, pos);\n var markers = [], spans = getLine(this, pos.line).markedSpans;\n if (spans) { for (var i = 0; i < spans.length; ++i) {\n var span = spans[i];\n if ((span.from == null || span.from <= pos.ch) &&\n (span.to == null || span.to >= pos.ch))\n { markers.push(span.marker.parent || span.marker); }\n } }\n return markers\n },\n findMarks: function(from, to, filter) {\n from = clipPos(this, from); to = clipPos(this, to);\n var found = [], lineNo = from.line;\n this.iter(from.line, to.line + 1, function (line) {\n var spans = line.markedSpans;\n if (spans) { for (var i = 0; i < spans.length; i++) {\n var span = spans[i];\n if (!(span.to != null && lineNo == from.line && from.ch >= span.to ||\n span.from == null && lineNo != from.line ||\n span.from != null && lineNo == to.line && span.from >= to.ch) &&\n (!filter || filter(span.marker)))\n { found.push(span.marker.parent || span.marker); }\n } }\n ++lineNo;\n });\n return found\n },\n getAllMarks: function() {\n var markers = [];\n this.iter(function (line) {\n var sps = line.markedSpans;\n if (sps) { for (var i = 0; i < sps.length; ++i)\n { if (sps[i].from != null) { markers.push(sps[i].marker); } } }\n });\n return markers\n },\n\n posFromIndex: function(off) {\n var ch, lineNo = this.first, sepSize = this.lineSeparator().length;\n this.iter(function (line) {\n var sz = line.text.length + sepSize;\n if (sz > off) { ch = off; return true }\n off -= sz;\n ++lineNo;\n });\n return clipPos(this, Pos(lineNo, ch))\n },\n indexFromPos: function (coords) {\n coords = clipPos(this, coords);\n var index = coords.ch;\n if (coords.line < this.first || coords.ch < 0) { return 0 }\n var sepSize = this.lineSeparator().length;\n this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value\n index += line.text.length + sepSize;\n });\n return index\n },\n\n copy: function(copyHistory) {\n var doc = new Doc(getLines(this, this.first, this.first + this.size),\n this.modeOption, this.first, this.lineSep, this.direction);\n doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;\n doc.sel = this.sel;\n doc.extend = false;\n if (copyHistory) {\n doc.history.undoDepth = this.history.undoDepth;\n doc.setHistory(this.getHistory());\n }\n return doc\n },\n\n linkedDoc: function(options) {\n if (!options) { options = {}; }\n var from = this.first, to = this.first + this.size;\n if (options.from != null && options.from > from) { from = options.from; }\n if (options.to != null && options.to < to) { to = options.to; }\n var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction);\n if (options.sharedHist) { copy.history = this.history\n ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});\n copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];\n copySharedMarkers(copy, findSharedMarkers(this));\n return copy\n },\n unlinkDoc: function(other) {\n if (other instanceof CodeMirror) { other = other.doc; }\n if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {\n var link = this.linked[i];\n if (link.doc != other) { continue }\n this.linked.splice(i, 1);\n other.unlinkDoc(this);\n detachSharedMarkers(findSharedMarkers(this));\n break\n } }\n // If the histories were shared, split them again\n if (other.history == this.history) {\n var splitIds = [other.id];\n linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true);\n other.history = new History(null);\n other.history.done = copyHistoryArray(this.history.done, splitIds);\n other.history.undone = copyHistoryArray(this.history.undone, splitIds);\n }\n },\n iterLinkedDocs: function(f) {linkedDocs(this, f);},\n\n getMode: function() {return this.mode},\n getEditor: function() {return this.cm},\n\n splitLines: function(str) {\n if (this.lineSep) { return str.split(this.lineSep) }\n return splitLinesAuto(str)\n },\n lineSeparator: function() { return this.lineSep || \"\\n\" },\n\n setDirection: docMethodOp(function (dir) {\n if (dir != \"rtl\") { dir = \"ltr\"; }\n if (dir == this.direction) { return }\n this.direction = dir;\n this.iter(function (line) { return line.order = null; });\n if (this.cm) { directionChanged(this.cm); }\n })\n });\n\n // Public alias.\n Doc.prototype.eachLine = Doc.prototype.iter;\n\n // Kludge to work around strange IE behavior where it'll sometimes\n // re-fire a series of drag-related events right after the drop (#1551)\n var lastDrop = 0;\n\n function onDrop(e) {\n var cm = this;\n clearDragCursor(cm);\n if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))\n { return }\n e_preventDefault(e);\n if (ie) { lastDrop = +new Date; }\n var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;\n if (!pos || cm.isReadOnly()) { return }\n // Might be a file drop, in which case we simply extract the text\n // and insert it.\n if (files && files.length && window.FileReader && window.File) {\n var n = files.length, text = Array(n), read = 0;\n var markAsReadAndPasteIfAllFilesAreRead = function () {\n if (++read == n) {\n operation(cm, function () {\n pos = clipPos(cm.doc, pos);\n var change = {from: pos, to: pos,\n text: cm.doc.splitLines(\n text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())),\n origin: \"paste\"};\n makeChange(cm.doc, change);\n setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));\n })();\n }\n };\n var readTextFromFile = function (file, i) {\n if (cm.options.allowDropFileTypes &&\n indexOf(cm.options.allowDropFileTypes, file.type) == -1) {\n markAsReadAndPasteIfAllFilesAreRead();\n return\n }\n var reader = new FileReader;\n reader.onerror = function () { return markAsReadAndPasteIfAllFilesAreRead(); };\n reader.onload = function () {\n var content = reader.result;\n if (/[\\x00-\\x08\\x0e-\\x1f]{2}/.test(content)) {\n markAsReadAndPasteIfAllFilesAreRead();\n return\n }\n text[i] = content;\n markAsReadAndPasteIfAllFilesAreRead();\n };\n reader.readAsText(file);\n };\n for (var i = 0; i < files.length; i++) { readTextFromFile(files[i], i); }\n } else { // Normal drop\n // Don't do a replace if the drop happened inside of the selected text.\n if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {\n cm.state.draggingText(e);\n // Ensure the editor is re-focused\n setTimeout(function () { return cm.display.input.focus(); }, 20);\n return\n }\n try {\n var text$1 = e.dataTransfer.getData(\"Text\");\n if (text$1) {\n var selected;\n if (cm.state.draggingText && !cm.state.draggingText.copy)\n { selected = cm.listSelections(); }\n setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));\n if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)\n { replaceRange(cm.doc, \"\", selected[i$1].anchor, selected[i$1].head, \"drag\"); } }\n cm.replaceSelection(text$1, \"around\", \"paste\");\n cm.display.input.focus();\n }\n }\n catch(e){}\n }\n }\n\n function onDragStart(cm, e) {\n if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }\n if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }\n\n e.dataTransfer.setData(\"Text\", cm.getSelection());\n e.dataTransfer.effectAllowed = \"copyMove\";\n\n // Use dummy image instead of default browsers image.\n // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.\n if (e.dataTransfer.setDragImage && !safari) {\n var img = elt(\"img\", null, null, \"position: fixed; left: 0; top: 0;\");\n img.src = \"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\";\n if (presto) {\n img.width = img.height = 1;\n cm.display.wrapper.appendChild(img);\n // Force a relayout, or Opera won't use our image for some obscure reason\n img._top = img.offsetTop;\n }\n e.dataTransfer.setDragImage(img, 0, 0);\n if (presto) { img.parentNode.removeChild(img); }\n }\n }\n\n function onDragOver(cm, e) {\n var pos = posFromMouse(cm, e);\n if (!pos) { return }\n var frag = document.createDocumentFragment();\n drawSelectionCursor(cm, pos, frag);\n if (!cm.display.dragCursor) {\n cm.display.dragCursor = elt(\"div\", null, \"CodeMirror-cursors CodeMirror-dragcursors\");\n cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);\n }\n removeChildrenAndAdd(cm.display.dragCursor, frag);\n }\n\n function clearDragCursor(cm) {\n if (cm.display.dragCursor) {\n cm.display.lineSpace.removeChild(cm.display.dragCursor);\n cm.display.dragCursor = null;\n }\n }\n\n // These must be handled carefully, because naively registering a\n // handler for each editor will cause the editors to never be\n // garbage collected.\n\n function forEachCodeMirror(f) {\n if (!document.getElementsByClassName) { return }\n var byClass = document.getElementsByClassName(\"CodeMirror\"), editors = [];\n for (var i = 0; i < byClass.length; i++) {\n var cm = byClass[i].CodeMirror;\n if (cm) { editors.push(cm); }\n }\n if (editors.length) { editors[0].operation(function () {\n for (var i = 0; i < editors.length; i++) { f(editors[i]); }\n }); }\n }\n\n var globalsRegistered = false;\n function ensureGlobalHandlers() {\n if (globalsRegistered) { return }\n registerGlobalHandlers();\n globalsRegistered = true;\n }\n function registerGlobalHandlers() {\n // When the window resizes, we need to refresh active editors.\n var resizeTimer;\n on(window, \"resize\", function () {\n if (resizeTimer == null) { resizeTimer = setTimeout(function () {\n resizeTimer = null;\n forEachCodeMirror(onResize);\n }, 100); }\n });\n // When the window loses focus, we want to show the editor as blurred\n on(window, \"blur\", function () { return forEachCodeMirror(onBlur); });\n }\n // Called when the window resizes\n function onResize(cm) {\n var d = cm.display;\n // Might be a text scaling operation, clear size caches.\n d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;\n d.scrollbarsClipped = false;\n cm.setSize();\n }\n\n var keyNames = {\n 3: \"Pause\", 8: \"Backspace\", 9: \"Tab\", 13: \"Enter\", 16: \"Shift\", 17: \"Ctrl\", 18: \"Alt\",\n 19: \"Pause\", 20: \"CapsLock\", 27: \"Esc\", 32: \"Space\", 33: \"PageUp\", 34: \"PageDown\", 35: \"End\",\n 36: \"Home\", 37: \"Left\", 38: \"Up\", 39: \"Right\", 40: \"Down\", 44: \"PrintScrn\", 45: \"Insert\",\n 46: \"Delete\", 59: \";\", 61: \"=\", 91: \"Mod\", 92: \"Mod\", 93: \"Mod\",\n 106: \"*\", 107: \"=\", 109: \"-\", 110: \".\", 111: \"/\", 145: \"ScrollLock\",\n 173: \"-\", 186: \";\", 187: \"=\", 188: \",\", 189: \"-\", 190: \".\", 191: \"/\", 192: \"`\", 219: \"[\", 220: \"\\\\\",\n 221: \"]\", 222: \"'\", 63232: \"Up\", 63233: \"Down\", 63234: \"Left\", 63235: \"Right\", 63272: \"Delete\",\n 63273: \"Home\", 63275: \"End\", 63276: \"PageUp\", 63277: \"PageDown\", 63302: \"Insert\"\n };\n\n // Number keys\n for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); }\n // Alphabetic keys\n for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); }\n // Function keys\n for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = \"F\" + i$2; }\n\n var keyMap = {};\n\n keyMap.basic = {\n \"Left\": \"goCharLeft\", \"Right\": \"goCharRight\", \"Up\": \"goLineUp\", \"Down\": \"goLineDown\",\n \"End\": \"goLineEnd\", \"Home\": \"goLineStartSmart\", \"PageUp\": \"goPageUp\", \"PageDown\": \"goPageDown\",\n \"Delete\": \"delCharAfter\", \"Backspace\": \"delCharBefore\", \"Shift-Backspace\": \"delCharBefore\",\n \"Tab\": \"defaultTab\", \"Shift-Tab\": \"indentAuto\",\n \"Enter\": \"newlineAndIndent\", \"Insert\": \"toggleOverwrite\",\n \"Esc\": \"singleSelection\"\n };\n // Note that the save and find-related commands aren't defined by\n // default. User code or addons can define them. Unknown commands\n // are simply ignored.\n keyMap.pcDefault = {\n \"Ctrl-A\": \"selectAll\", \"Ctrl-D\": \"deleteLine\", \"Ctrl-Z\": \"undo\", \"Shift-Ctrl-Z\": \"redo\", \"Ctrl-Y\": \"redo\",\n \"Ctrl-Home\": \"goDocStart\", \"Ctrl-End\": \"goDocEnd\", \"Ctrl-Up\": \"goLineUp\", \"Ctrl-Down\": \"goLineDown\",\n \"Ctrl-Left\": \"goGroupLeft\", \"Ctrl-Right\": \"goGroupRight\", \"Alt-Left\": \"goLineStart\", \"Alt-Right\": \"goLineEnd\",\n \"Ctrl-Backspace\": \"delGroupBefore\", \"Ctrl-Delete\": \"delGroupAfter\", \"Ctrl-S\": \"save\", \"Ctrl-F\": \"find\",\n \"Ctrl-G\": \"findNext\", \"Shift-Ctrl-G\": \"findPrev\", \"Shift-Ctrl-F\": \"replace\", \"Shift-Ctrl-R\": \"replaceAll\",\n \"Ctrl-[\": \"indentLess\", \"Ctrl-]\": \"indentMore\",\n \"Ctrl-U\": \"undoSelection\", \"Shift-Ctrl-U\": \"redoSelection\", \"Alt-U\": \"redoSelection\",\n \"fallthrough\": \"basic\"\n };\n // Very basic readline/emacs-style bindings, which are standard on Mac.\n keyMap.emacsy = {\n \"Ctrl-F\": \"goCharRight\", \"Ctrl-B\": \"goCharLeft\", \"Ctrl-P\": \"goLineUp\", \"Ctrl-N\": \"goLineDown\",\n \"Alt-F\": \"goWordRight\", \"Alt-B\": \"goWordLeft\", \"Ctrl-A\": \"goLineStart\", \"Ctrl-E\": \"goLineEnd\",\n \"Ctrl-V\": \"goPageDown\", \"Shift-Ctrl-V\": \"goPageUp\", \"Ctrl-D\": \"delCharAfter\", \"Ctrl-H\": \"delCharBefore\",\n \"Alt-D\": \"delWordAfter\", \"Alt-Backspace\": \"delWordBefore\", \"Ctrl-K\": \"killLine\", \"Ctrl-T\": \"transposeChars\",\n \"Ctrl-O\": \"openLine\"\n };\n keyMap.macDefault = {\n \"Cmd-A\": \"selectAll\", \"Cmd-D\": \"deleteLine\", \"Cmd-Z\": \"undo\", \"Shift-Cmd-Z\": \"redo\", \"Cmd-Y\": \"redo\",\n \"Cmd-Home\": \"goDocStart\", \"Cmd-Up\": \"goDocStart\", \"Cmd-End\": \"goDocEnd\", \"Cmd-Down\": \"goDocEnd\", \"Alt-Left\": \"goGroupLeft\",\n \"Alt-Right\": \"goGroupRight\", \"Cmd-Left\": \"goLineLeft\", \"Cmd-Right\": \"goLineRight\", \"Alt-Backspace\": \"delGroupBefore\",\n \"Ctrl-Alt-Backspace\": \"delGroupAfter\", \"Alt-Delete\": \"delGroupAfter\", \"Cmd-S\": \"save\", \"Cmd-F\": \"find\",\n \"Cmd-G\": \"findNext\", \"Shift-Cmd-G\": \"findPrev\", \"Cmd-Alt-F\": \"replace\", \"Shift-Cmd-Alt-F\": \"replaceAll\",\n \"Cmd-[\": \"indentLess\", \"Cmd-]\": \"indentMore\", \"Cmd-Backspace\": \"delWrappedLineLeft\", \"Cmd-Delete\": \"delWrappedLineRight\",\n \"Cmd-U\": \"undoSelection\", \"Shift-Cmd-U\": \"redoSelection\", \"Ctrl-Up\": \"goDocStart\", \"Ctrl-Down\": \"goDocEnd\",\n \"fallthrough\": [\"basic\", \"emacsy\"]\n };\n keyMap[\"default\"] = mac ? keyMap.macDefault : keyMap.pcDefault;\n\n // KEYMAP DISPATCH\n\n function normalizeKeyName(name) {\n var parts = name.split(/-(?!$)/);\n name = parts[parts.length - 1];\n var alt, ctrl, shift, cmd;\n for (var i = 0; i < parts.length - 1; i++) {\n var mod = parts[i];\n if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; }\n else if (/^a(lt)?$/i.test(mod)) { alt = true; }\n else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; }\n else if (/^s(hift)?$/i.test(mod)) { shift = true; }\n else { throw new Error(\"Unrecognized modifier name: \" + mod) }\n }\n if (alt) { name = \"Alt-\" + name; }\n if (ctrl) { name = \"Ctrl-\" + name; }\n if (cmd) { name = \"Cmd-\" + name; }\n if (shift) { name = \"Shift-\" + name; }\n return name\n }\n\n // This is a kludge to keep keymaps mostly working as raw objects\n // (backwards compatibility) while at the same time support features\n // like normalization and multi-stroke key bindings. It compiles a\n // new normalized keymap, and then updates the old object to reflect\n // this.\n function normalizeKeyMap(keymap) {\n var copy = {};\n for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {\n var value = keymap[keyname];\n if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }\n if (value == \"...\") { delete keymap[keyname]; continue }\n\n var keys = map(keyname.split(\" \"), normalizeKeyName);\n for (var i = 0; i < keys.length; i++) {\n var val = (void 0), name = (void 0);\n if (i == keys.length - 1) {\n name = keys.join(\" \");\n val = value;\n } else {\n name = keys.slice(0, i + 1).join(\" \");\n val = \"...\";\n }\n var prev = copy[name];\n if (!prev) { copy[name] = val; }\n else if (prev != val) { throw new Error(\"Inconsistent bindings for \" + name) }\n }\n delete keymap[keyname];\n } }\n for (var prop in copy) { keymap[prop] = copy[prop]; }\n return keymap\n }\n\n function lookupKey(key, map, handle, context) {\n map = getKeyMap(map);\n var found = map.call ? map.call(key, context) : map[key];\n if (found === false) { return \"nothing\" }\n if (found === \"...\") { return \"multi\" }\n if (found != null && handle(found)) { return \"handled\" }\n\n if (map.fallthrough) {\n if (Object.prototype.toString.call(map.fallthrough) != \"[object Array]\")\n { return lookupKey(key, map.fallthrough, handle, context) }\n for (var i = 0; i < map.fallthrough.length; i++) {\n var result = lookupKey(key, map.fallthrough[i], handle, context);\n if (result) { return result }\n }\n }\n }\n\n // Modifier key presses don't count as 'real' key presses for the\n // purpose of keymap fallthrough.\n function isModifierKey(value) {\n var name = typeof value == \"string\" ? value : keyNames[value.keyCode];\n return name == \"Ctrl\" || name == \"Alt\" || name == \"Shift\" || name == \"Mod\"\n }\n\n function addModifierNames(name, event, noShift) {\n var base = name;\n if (event.altKey && base != \"Alt\") { name = \"Alt-\" + name; }\n if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != \"Ctrl\") { name = \"Ctrl-\" + name; }\n if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != \"Cmd\") { name = \"Cmd-\" + name; }\n if (!noShift && event.shiftKey && base != \"Shift\") { name = \"Shift-\" + name; }\n return name\n }\n\n // Look up the name of a key as indicated by an event object.\n function keyName(event, noShift) {\n if (presto && event.keyCode == 34 && event[\"char\"]) { return false }\n var name = keyNames[event.keyCode];\n if (name == null || event.altGraphKey) { return false }\n // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,\n // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)\n if (event.keyCode == 3 && event.code) { name = event.code; }\n return addModifierNames(name, event, noShift)\n }\n\n function getKeyMap(val) {\n return typeof val == \"string\" ? keyMap[val] : val\n }\n\n // Helper for deleting text near the selection(s), used to implement\n // backspace, delete, and similar functionality.\n function deleteNearSelection(cm, compute) {\n var ranges = cm.doc.sel.ranges, kill = [];\n // Build up a set of ranges to kill first, merging overlapping\n // ranges.\n for (var i = 0; i < ranges.length; i++) {\n var toKill = compute(ranges[i]);\n while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {\n var replaced = kill.pop();\n if (cmp(replaced.from, toKill.from) < 0) {\n toKill.from = replaced.from;\n break\n }\n }\n kill.push(toKill);\n }\n // Next, remove those actual ranges.\n runInOp(cm, function () {\n for (var i = kill.length - 1; i >= 0; i--)\n { replaceRange(cm.doc, \"\", kill[i].from, kill[i].to, \"+delete\"); }\n ensureCursorVisible(cm);\n });\n }\n\n function moveCharLogically(line, ch, dir) {\n var target = skipExtendingChars(line.text, ch + dir, dir);\n return target < 0 || target > line.text.length ? null : target\n }\n\n function moveLogically(line, start, dir) {\n var ch = moveCharLogically(line, start.ch, dir);\n return ch == null ? null : new Pos(start.line, ch, dir < 0 ? \"after\" : \"before\")\n }\n\n function endOfLine(visually, cm, lineObj, lineNo, dir) {\n if (visually) {\n if (cm.getOption(\"direction\") == \"rtl\") { dir = -dir; }\n var order = getOrder(lineObj, cm.doc.direction);\n if (order) {\n var part = dir < 0 ? lst(order) : order[0];\n var moveInStorageOrder = (dir < 0) == (part.level == 1);\n var sticky = moveInStorageOrder ? \"after\" : \"before\";\n var ch;\n // With a wrapped rtl chunk (possibly spanning multiple bidi parts),\n // it could be that the last bidi part is not on the last visual line,\n // since visual lines contain content order-consecutive chunks.\n // Thus, in rtl, we are looking for the first (content-order) character\n // in the rtl chunk that is on the last line (that is, the same line\n // as the last (content-order) character).\n if (part.level > 0 || cm.doc.direction == \"rtl\") {\n var prep = prepareMeasureForLine(cm, lineObj);\n ch = dir < 0 ? lineObj.text.length - 1 : 0;\n var targetTop = measureCharPrepared(cm, prep, ch).top;\n ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch);\n if (sticky == \"before\") { ch = moveCharLogically(lineObj, ch, 1); }\n } else { ch = dir < 0 ? part.to : part.from; }\n return new Pos(lineNo, ch, sticky)\n }\n }\n return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? \"before\" : \"after\")\n }\n\n function moveVisually(cm, line, start, dir) {\n var bidi = getOrder(line, cm.doc.direction);\n if (!bidi) { return moveLogically(line, start, dir) }\n if (start.ch >= line.text.length) {\n start.ch = line.text.length;\n start.sticky = \"before\";\n } else if (start.ch <= 0) {\n start.ch = 0;\n start.sticky = \"after\";\n }\n var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos];\n if (cm.doc.direction == \"ltr\" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {\n // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,\n // nothing interesting happens.\n return moveLogically(line, start, dir)\n }\n\n var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); };\n var prep;\n var getWrappedLineExtent = function (ch) {\n if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }\n prep = prep || prepareMeasureForLine(cm, line);\n return wrappedLineExtentChar(cm, line, prep, ch)\n };\n var wrappedLineExtent = getWrappedLineExtent(start.sticky == \"before\" ? mv(start, -1) : start.ch);\n\n if (cm.doc.direction == \"rtl\" || part.level == 1) {\n var moveInStorageOrder = (part.level == 1) == (dir < 0);\n var ch = mv(start, moveInStorageOrder ? 1 : -1);\n if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {\n // Case 2: We move within an rtl part or in an rtl editor on the same visual line\n var sticky = moveInStorageOrder ? \"before\" : \"after\";\n return new Pos(start.line, ch, sticky)\n }\n }\n\n // Case 3: Could not move within this bidi part in this visual line, so leave\n // the current bidi part\n\n var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {\n var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder\n ? new Pos(start.line, mv(ch, 1), \"before\")\n : new Pos(start.line, ch, \"after\"); };\n\n for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {\n var part = bidi[partPos];\n var moveInStorageOrder = (dir > 0) == (part.level != 1);\n var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1);\n if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }\n ch = moveInStorageOrder ? part.from : mv(part.to, -1);\n if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }\n }\n };\n\n // Case 3a: Look for other bidi parts on the same visual line\n var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent);\n if (res) { return res }\n\n // Case 3b: Look for other bidi parts on the next visual line\n var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1);\n if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {\n res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh));\n if (res) { return res }\n }\n\n // Case 4: Nowhere to move\n return null\n }\n\n // Commands are parameter-less actions that can be performed on an\n // editor, mostly used for keybindings.\n var commands = {\n selectAll: selectAll,\n singleSelection: function (cm) { return cm.setSelection(cm.getCursor(\"anchor\"), cm.getCursor(\"head\"), sel_dontScroll); },\n killLine: function (cm) { return deleteNearSelection(cm, function (range) {\n if (range.empty()) {\n var len = getLine(cm.doc, range.head.line).text.length;\n if (range.head.ch == len && range.head.line < cm.lastLine())\n { return {from: range.head, to: Pos(range.head.line + 1, 0)} }\n else\n { return {from: range.head, to: Pos(range.head.line, len)} }\n } else {\n return {from: range.from(), to: range.to()}\n }\n }); },\n deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({\n from: Pos(range.from().line, 0),\n to: clipPos(cm.doc, Pos(range.to().line + 1, 0))\n }); }); },\n delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({\n from: Pos(range.from().line, 0), to: range.from()\n }); }); },\n delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {\n var top = cm.charCoords(range.head, \"div\").top + 5;\n var leftPos = cm.coordsChar({left: 0, top: top}, \"div\");\n return {from: leftPos, to: range.from()}\n }); },\n delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {\n var top = cm.charCoords(range.head, \"div\").top + 5;\n var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, \"div\");\n return {from: range.from(), to: rightPos }\n }); },\n undo: function (cm) { return cm.undo(); },\n redo: function (cm) { return cm.redo(); },\n undoSelection: function (cm) { return cm.undoSelection(); },\n redoSelection: function (cm) { return cm.redoSelection(); },\n goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },\n goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },\n goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },\n {origin: \"+move\", bias: 1}\n ); },\n goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },\n {origin: \"+move\", bias: 1}\n ); },\n goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },\n {origin: \"+move\", bias: -1}\n ); },\n goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {\n var top = cm.cursorCoords(range.head, \"div\").top + 5;\n return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, \"div\")\n }, sel_move); },\n goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {\n var top = cm.cursorCoords(range.head, \"div\").top + 5;\n return cm.coordsChar({left: 0, top: top}, \"div\")\n }, sel_move); },\n goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {\n var top = cm.cursorCoords(range.head, \"div\").top + 5;\n var pos = cm.coordsChar({left: 0, top: top}, \"div\");\n if (pos.ch < cm.getLine(pos.line).search(/\\S/)) { return lineStartSmart(cm, range.head) }\n return pos\n }, sel_move); },\n goLineUp: function (cm) { return cm.moveV(-1, \"line\"); },\n goLineDown: function (cm) { return cm.moveV(1, \"line\"); },\n goPageUp: function (cm) { return cm.moveV(-1, \"page\"); },\n goPageDown: function (cm) { return cm.moveV(1, \"page\"); },\n goCharLeft: function (cm) { return cm.moveH(-1, \"char\"); },\n goCharRight: function (cm) { return cm.moveH(1, \"char\"); },\n goColumnLeft: function (cm) { return cm.moveH(-1, \"column\"); },\n goColumnRight: function (cm) { return cm.moveH(1, \"column\"); },\n goWordLeft: function (cm) { return cm.moveH(-1, \"word\"); },\n goGroupRight: function (cm) { return cm.moveH(1, \"group\"); },\n goGroupLeft: function (cm) { return cm.moveH(-1, \"group\"); },\n goWordRight: function (cm) { return cm.moveH(1, \"word\"); },\n delCharBefore: function (cm) { return cm.deleteH(-1, \"char\"); },\n delCharAfter: function (cm) { return cm.deleteH(1, \"char\"); },\n delWordBefore: function (cm) { return cm.deleteH(-1, \"word\"); },\n delWordAfter: function (cm) { return cm.deleteH(1, \"word\"); },\n delGroupBefore: function (cm) { return cm.deleteH(-1, \"group\"); },\n delGroupAfter: function (cm) { return cm.deleteH(1, \"group\"); },\n indentAuto: function (cm) { return cm.indentSelection(\"smart\"); },\n indentMore: function (cm) { return cm.indentSelection(\"add\"); },\n indentLess: function (cm) { return cm.indentSelection(\"subtract\"); },\n insertTab: function (cm) { return cm.replaceSelection(\"\\t\"); },\n insertSoftTab: function (cm) {\n var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;\n for (var i = 0; i < ranges.length; i++) {\n var pos = ranges[i].from();\n var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);\n spaces.push(spaceStr(tabSize - col % tabSize));\n }\n cm.replaceSelections(spaces);\n },\n defaultTab: function (cm) {\n if (cm.somethingSelected()) { cm.indentSelection(\"add\"); }\n else { cm.execCommand(\"insertTab\"); }\n },\n // Swap the two chars left and right of each selection's head.\n // Move cursor behind the two swapped characters afterwards.\n //\n // Doesn't consider line feeds a character.\n // Doesn't scan more than one line above to find a character.\n // Doesn't do anything on an empty line.\n // Doesn't do anything with non-empty selections.\n transposeChars: function (cm) { return runInOp(cm, function () {\n var ranges = cm.listSelections(), newSel = [];\n for (var i = 0; i < ranges.length; i++) {\n if (!ranges[i].empty()) { continue }\n var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;\n if (line) {\n if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); }\n if (cur.ch > 0) {\n cur = new Pos(cur.line, cur.ch + 1);\n cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),\n Pos(cur.line, cur.ch - 2), cur, \"+transpose\");\n } else if (cur.line > cm.doc.first) {\n var prev = getLine(cm.doc, cur.line - 1).text;\n if (prev) {\n cur = new Pos(cur.line, 1);\n cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +\n prev.charAt(prev.length - 1),\n Pos(cur.line - 1, prev.length - 1), cur, \"+transpose\");\n }\n }\n }\n newSel.push(new Range(cur, cur));\n }\n cm.setSelections(newSel);\n }); },\n newlineAndIndent: function (cm) { return runInOp(cm, function () {\n var sels = cm.listSelections();\n for (var i = sels.length - 1; i >= 0; i--)\n { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, \"+input\"); }\n sels = cm.listSelections();\n for (var i$1 = 0; i$1 < sels.length; i$1++)\n { cm.indentLine(sels[i$1].from().line, null, true); }\n ensureCursorVisible(cm);\n }); },\n openLine: function (cm) { return cm.replaceSelection(\"\\n\", \"start\"); },\n toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }\n };\n\n\n function lineStart(cm, lineN) {\n var line = getLine(cm.doc, lineN);\n var visual = visualLine(line);\n if (visual != line) { lineN = lineNo(visual); }\n return endOfLine(true, cm, visual, lineN, 1)\n }\n function lineEnd(cm, lineN) {\n var line = getLine(cm.doc, lineN);\n var visual = visualLineEnd(line);\n if (visual != line) { lineN = lineNo(visual); }\n return endOfLine(true, cm, line, lineN, -1)\n }\n function lineStartSmart(cm, pos) {\n var start = lineStart(cm, pos.line);\n var line = getLine(cm.doc, start.line);\n var order = getOrder(line, cm.doc.direction);\n if (!order || order[0].level == 0) {\n var firstNonWS = Math.max(0, line.text.search(/\\S/));\n var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;\n return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)\n }\n return start\n }\n\n // Run a handler that was bound to a key.\n function doHandleBinding(cm, bound, dropShift) {\n if (typeof bound == \"string\") {\n bound = commands[bound];\n if (!bound) { return false }\n }\n // Ensure previous input has been read, so that the handler sees a\n // consistent view of the document\n cm.display.input.ensurePolled();\n var prevShift = cm.display.shift, done = false;\n try {\n if (cm.isReadOnly()) { cm.state.suppressEdits = true; }\n if (dropShift) { cm.display.shift = false; }\n done = bound(cm) != Pass;\n } finally {\n cm.display.shift = prevShift;\n cm.state.suppressEdits = false;\n }\n return done\n }\n\n function lookupKeyForEditor(cm, name, handle) {\n for (var i = 0; i < cm.state.keyMaps.length; i++) {\n var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);\n if (result) { return result }\n }\n return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))\n || lookupKey(name, cm.options.keyMap, handle, cm)\n }\n\n // Note that, despite the name, this function is also used to check\n // for bound mouse clicks.\n\n var stopSeq = new Delayed;\n\n function dispatchKey(cm, name, e, handle) {\n var seq = cm.state.keySeq;\n if (seq) {\n if (isModifierKey(name)) { return \"handled\" }\n if (/\\'$/.test(name))\n { cm.state.keySeq = null; }\n else\n { stopSeq.set(50, function () {\n if (cm.state.keySeq == seq) {\n cm.state.keySeq = null;\n cm.display.input.reset();\n }\n }); }\n if (dispatchKeyInner(cm, seq + \" \" + name, e, handle)) { return true }\n }\n return dispatchKeyInner(cm, name, e, handle)\n }\n\n function dispatchKeyInner(cm, name, e, handle) {\n var result = lookupKeyForEditor(cm, name, handle);\n\n if (result == \"multi\")\n { cm.state.keySeq = name; }\n if (result == \"handled\")\n { signalLater(cm, \"keyHandled\", cm, name, e); }\n\n if (result == \"handled\" || result == \"multi\") {\n e_preventDefault(e);\n restartBlink(cm);\n }\n\n return !!result\n }\n\n // Handle a key from the keydown event.\n function handleKeyBinding(cm, e) {\n var name = keyName(e, true);\n if (!name) { return false }\n\n if (e.shiftKey && !cm.state.keySeq) {\n // First try to resolve full name (including 'Shift-'). Failing\n // that, see if there is a cursor-motion command (starting with\n // 'go') bound to the keyname without 'Shift-'.\n return dispatchKey(cm, \"Shift-\" + name, e, function (b) { return doHandleBinding(cm, b, true); })\n || dispatchKey(cm, name, e, function (b) {\n if (typeof b == \"string\" ? /^go[A-Z]/.test(b) : b.motion)\n { return doHandleBinding(cm, b) }\n })\n } else {\n return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })\n }\n }\n\n // Handle a key from the keypress event\n function handleCharBinding(cm, e, ch) {\n return dispatchKey(cm, \"'\" + ch + \"'\", e, function (b) { return doHandleBinding(cm, b, true); })\n }\n\n var lastStoppedKey = null;\n function onKeyDown(e) {\n var cm = this;\n cm.curOp.focus = activeElt();\n if (signalDOMEvent(cm, e)) { return }\n // IE does strange things with escape.\n if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; }\n var code = e.keyCode;\n cm.display.shift = code == 16 || e.shiftKey;\n var handled = handleKeyBinding(cm, e);\n if (presto) {\n lastStoppedKey = handled ? code : null;\n // Opera has no cut event... we try to at least catch the key combo\n if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))\n { cm.replaceSelection(\"\", null, \"cut\"); }\n }\n if (gecko && !mac && !handled && code == 46 && e.shiftKey && !e.ctrlKey && document.execCommand)\n { document.execCommand(\"cut\"); }\n\n // Turn mouse into crosshair when Alt is held on Mac.\n if (code == 18 && !/\\bCodeMirror-crosshair\\b/.test(cm.display.lineDiv.className))\n { showCrossHair(cm); }\n }\n\n function showCrossHair(cm) {\n var lineDiv = cm.display.lineDiv;\n addClass(lineDiv, \"CodeMirror-crosshair\");\n\n function up(e) {\n if (e.keyCode == 18 || !e.altKey) {\n rmClass(lineDiv, \"CodeMirror-crosshair\");\n off(document, \"keyup\", up);\n off(document, \"mouseover\", up);\n }\n }\n on(document, \"keyup\", up);\n on(document, \"mouseover\", up);\n }\n\n function onKeyUp(e) {\n if (e.keyCode == 16) { this.doc.sel.shift = false; }\n signalDOMEvent(this, e);\n }\n\n function onKeyPress(e) {\n var cm = this;\n if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }\n var keyCode = e.keyCode, charCode = e.charCode;\n if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}\n if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }\n var ch = String.fromCharCode(charCode == null ? keyCode : charCode);\n // Some browsers fire keypress events for backspace\n if (ch == \"\\x08\") { return }\n if (handleCharBinding(cm, e, ch)) { return }\n cm.display.input.onKeyPress(e);\n }\n\n var DOUBLECLICK_DELAY = 400;\n\n var PastClick = function(time, pos, button) {\n this.time = time;\n this.pos = pos;\n this.button = button;\n };\n\n PastClick.prototype.compare = function (time, pos, button) {\n return this.time + DOUBLECLICK_DELAY > time &&\n cmp(pos, this.pos) == 0 && button == this.button\n };\n\n var lastClick, lastDoubleClick;\n function clickRepeat(pos, button) {\n var now = +new Date;\n if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {\n lastClick = lastDoubleClick = null;\n return \"triple\"\n } else if (lastClick && lastClick.compare(now, pos, button)) {\n lastDoubleClick = new PastClick(now, pos, button);\n lastClick = null;\n return \"double\"\n } else {\n lastClick = new PastClick(now, pos, button);\n lastDoubleClick = null;\n return \"single\"\n }\n }\n\n // A mouse down can be a single click, double click, triple click,\n // start of selection drag, start of text drag, new cursor\n // (ctrl-click), rectangle drag (alt-drag), or xwin\n // middle-click-paste. Or it might be a click on something we should\n // not interfere with, such as a scrollbar or widget.\n function onMouseDown(e) {\n var cm = this, display = cm.display;\n if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }\n display.input.ensurePolled();\n display.shift = e.shiftKey;\n\n if (eventInWidget(display, e)) {\n if (!webkit) {\n // Briefly turn off draggability, to allow widgets to do\n // normal dragging things.\n display.scroller.draggable = false;\n setTimeout(function () { return display.scroller.draggable = true; }, 100);\n }\n return\n }\n if (clickInGutter(cm, e)) { return }\n var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : \"single\";\n window.focus();\n\n // #3261: make sure, that we're not starting a second selection\n if (button == 1 && cm.state.selectingText)\n { cm.state.selectingText(e); }\n\n if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }\n\n if (button == 1) {\n if (pos) { leftButtonDown(cm, pos, repeat, e); }\n else if (e_target(e) == display.scroller) { e_preventDefault(e); }\n } else if (button == 2) {\n if (pos) { extendSelection(cm.doc, pos); }\n setTimeout(function () { return display.input.focus(); }, 20);\n } else if (button == 3) {\n if (captureRightClick) { cm.display.input.onContextMenu(e); }\n else { delayBlurEvent(cm); }\n }\n }\n\n function handleMappedButton(cm, button, pos, repeat, event) {\n var name = \"Click\";\n if (repeat == \"double\") { name = \"Double\" + name; }\n else if (repeat == \"triple\") { name = \"Triple\" + name; }\n name = (button == 1 ? \"Left\" : button == 2 ? \"Middle\" : \"Right\") + name;\n\n return dispatchKey(cm, addModifierNames(name, event), event, function (bound) {\n if (typeof bound == \"string\") { bound = commands[bound]; }\n if (!bound) { return false }\n var done = false;\n try {\n if (cm.isReadOnly()) { cm.state.suppressEdits = true; }\n done = bound(cm, pos) != Pass;\n } finally {\n cm.state.suppressEdits = false;\n }\n return done\n })\n }\n\n function configureMouse(cm, repeat, event) {\n var option = cm.getOption(\"configureMouse\");\n var value = option ? option(cm, repeat, event) : {};\n if (value.unit == null) {\n var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey;\n value.unit = rect ? \"rectangle\" : repeat == \"single\" ? \"char\" : repeat == \"double\" ? \"word\" : \"line\";\n }\n if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; }\n if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; }\n if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); }\n return value\n }\n\n function leftButtonDown(cm, pos, repeat, event) {\n if (ie) { setTimeout(bind(ensureFocus, cm), 0); }\n else { cm.curOp.focus = activeElt(); }\n\n var behavior = configureMouse(cm, repeat, event);\n\n var sel = cm.doc.sel, contained;\n if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&\n repeat == \"single\" && (contained = sel.contains(pos)) > -1 &&\n (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&\n (cmp(contained.to(), pos) > 0 || pos.xRel < 0))\n { leftButtonStartDrag(cm, event, pos, behavior); }\n else\n { leftButtonSelect(cm, event, pos, behavior); }\n }\n\n // Start a text drag. When it ends, see if any dragging actually\n // happen, and treat as a click if it didn't.\n function leftButtonStartDrag(cm, event, pos, behavior) {\n var display = cm.display, moved = false;\n var dragEnd = operation(cm, function (e) {\n if (webkit) { display.scroller.draggable = false; }\n cm.state.draggingText = false;\n off(display.wrapper.ownerDocument, \"mouseup\", dragEnd);\n off(display.wrapper.ownerDocument, \"mousemove\", mouseMove);\n off(display.scroller, \"dragstart\", dragStart);\n off(display.scroller, \"drop\", dragEnd);\n if (!moved) {\n e_preventDefault(e);\n if (!behavior.addNew)\n { extendSelection(cm.doc, pos, null, null, behavior.extend); }\n // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)\n if (webkit || ie && ie_version == 9)\n { setTimeout(function () {display.wrapper.ownerDocument.body.focus(); display.input.focus();}, 20); }\n else\n { display.input.focus(); }\n }\n });\n var mouseMove = function(e2) {\n moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10;\n };\n var dragStart = function () { return moved = true; };\n // Let the drag handler handle this.\n if (webkit) { display.scroller.draggable = true; }\n cm.state.draggingText = dragEnd;\n dragEnd.copy = !behavior.moveOnDrag;\n // IE's approach to draggable\n if (display.scroller.dragDrop) { display.scroller.dragDrop(); }\n on(display.wrapper.ownerDocument, \"mouseup\", dragEnd);\n on(display.wrapper.ownerDocument, \"mousemove\", mouseMove);\n on(display.scroller, \"dragstart\", dragStart);\n on(display.scroller, \"drop\", dragEnd);\n\n delayBlurEvent(cm);\n setTimeout(function () { return display.input.focus(); }, 20);\n }\n\n function rangeForUnit(cm, pos, unit) {\n if (unit == \"char\") { return new Range(pos, pos) }\n if (unit == \"word\") { return cm.findWordAt(pos) }\n if (unit == \"line\") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }\n var result = unit(cm, pos);\n return new Range(result.from, result.to)\n }\n\n // Normal selection, as opposed to text dragging.\n function leftButtonSelect(cm, event, start, behavior) {\n var display = cm.display, doc = cm.doc;\n e_preventDefault(event);\n\n var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;\n if (behavior.addNew && !behavior.extend) {\n ourIndex = doc.sel.contains(start);\n if (ourIndex > -1)\n { ourRange = ranges[ourIndex]; }\n else\n { ourRange = new Range(start, start); }\n } else {\n ourRange = doc.sel.primary();\n ourIndex = doc.sel.primIndex;\n }\n\n if (behavior.unit == \"rectangle\") {\n if (!behavior.addNew) { ourRange = new Range(start, start); }\n start = posFromMouse(cm, event, true, true);\n ourIndex = -1;\n } else {\n var range = rangeForUnit(cm, start, behavior.unit);\n if (behavior.extend)\n { ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend); }\n else\n { ourRange = range; }\n }\n\n if (!behavior.addNew) {\n ourIndex = 0;\n setSelection(doc, new Selection([ourRange], 0), sel_mouse);\n startSel = doc.sel;\n } else if (ourIndex == -1) {\n ourIndex = ranges.length;\n setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex),\n {scroll: false, origin: \"*mouse\"});\n } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == \"char\" && !behavior.extend) {\n setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),\n {scroll: false, origin: \"*mouse\"});\n startSel = doc.sel;\n } else {\n replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);\n }\n\n var lastPos = start;\n function extendTo(pos) {\n if (cmp(lastPos, pos) == 0) { return }\n lastPos = pos;\n\n if (behavior.unit == \"rectangle\") {\n var ranges = [], tabSize = cm.options.tabSize;\n var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);\n var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);\n var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);\n for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));\n line <= end; line++) {\n var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);\n if (left == right)\n { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); }\n else if (text.length > leftPos)\n { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); }\n }\n if (!ranges.length) { ranges.push(new Range(start, start)); }\n setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),\n {origin: \"*mouse\", scroll: false});\n cm.scrollIntoView(pos);\n } else {\n var oldRange = ourRange;\n var range = rangeForUnit(cm, pos, behavior.unit);\n var anchor = oldRange.anchor, head;\n if (cmp(range.anchor, anchor) > 0) {\n head = range.head;\n anchor = minPos(oldRange.from(), range.anchor);\n } else {\n head = range.anchor;\n anchor = maxPos(oldRange.to(), range.head);\n }\n var ranges$1 = startSel.ranges.slice(0);\n ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head));\n setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse);\n }\n }\n\n var editorSize = display.wrapper.getBoundingClientRect();\n // Used to ensure timeout re-tries don't fire when another extend\n // happened in the meantime (clearTimeout isn't reliable -- at\n // least on Chrome, the timeouts still happen even when cleared,\n // if the clear happens after their scheduled firing time).\n var counter = 0;\n\n function extend(e) {\n var curCount = ++counter;\n var cur = posFromMouse(cm, e, true, behavior.unit == \"rectangle\");\n if (!cur) { return }\n if (cmp(cur, lastPos) != 0) {\n cm.curOp.focus = activeElt();\n extendTo(cur);\n var visible = visibleLines(display, doc);\n if (cur.line >= visible.to || cur.line < visible.from)\n { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); }\n } else {\n var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;\n if (outside) { setTimeout(operation(cm, function () {\n if (counter != curCount) { return }\n display.scroller.scrollTop += outside;\n extend(e);\n }), 50); }\n }\n }\n\n function done(e) {\n cm.state.selectingText = false;\n counter = Infinity;\n // If e is null or undefined we interpret this as someone trying\n // to explicitly cancel the selection rather than the user\n // letting go of the mouse button.\n if (e) {\n e_preventDefault(e);\n display.input.focus();\n }\n off(display.wrapper.ownerDocument, \"mousemove\", move);\n off(display.wrapper.ownerDocument, \"mouseup\", up);\n doc.history.lastSelOrigin = null;\n }\n\n var move = operation(cm, function (e) {\n if (e.buttons === 0 || !e_button(e)) { done(e); }\n else { extend(e); }\n });\n var up = operation(cm, done);\n cm.state.selectingText = up;\n on(display.wrapper.ownerDocument, \"mousemove\", move);\n on(display.wrapper.ownerDocument, \"mouseup\", up);\n }\n\n // Used when mouse-selecting to adjust the anchor to the proper side\n // of a bidi jump depending on the visual position of the head.\n function bidiSimplify(cm, range) {\n var anchor = range.anchor;\n var head = range.head;\n var anchorLine = getLine(cm.doc, anchor.line);\n if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range }\n var order = getOrder(anchorLine);\n if (!order) { return range }\n var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index];\n if (part.from != anchor.ch && part.to != anchor.ch) { return range }\n var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1);\n if (boundary == 0 || boundary == order.length) { return range }\n\n // Compute the relative visual position of the head compared to the\n // anchor (<0 is to the left, >0 to the right)\n var leftSide;\n if (head.line != anchor.line) {\n leftSide = (head.line - anchor.line) * (cm.doc.direction == \"ltr\" ? 1 : -1) > 0;\n } else {\n var headIndex = getBidiPartAt(order, head.ch, head.sticky);\n var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1);\n if (headIndex == boundary - 1 || headIndex == boundary)\n { leftSide = dir < 0; }\n else\n { leftSide = dir > 0; }\n }\n\n var usePart = order[boundary + (leftSide ? -1 : 0)];\n var from = leftSide == (usePart.level == 1);\n var ch = from ? usePart.from : usePart.to, sticky = from ? \"after\" : \"before\";\n return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head)\n }\n\n\n // Determines whether an event happened in the gutter, and fires the\n // handlers for the corresponding event.\n function gutterEvent(cm, e, type, prevent) {\n var mX, mY;\n if (e.touches) {\n mX = e.touches[0].clientX;\n mY = e.touches[0].clientY;\n } else {\n try { mX = e.clientX; mY = e.clientY; }\n catch(e) { return false }\n }\n if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }\n if (prevent) { e_preventDefault(e); }\n\n var display = cm.display;\n var lineBox = display.lineDiv.getBoundingClientRect();\n\n if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }\n mY -= lineBox.top - display.viewOffset;\n\n for (var i = 0; i < cm.display.gutterSpecs.length; ++i) {\n var g = display.gutters.childNodes[i];\n if (g && g.getBoundingClientRect().right >= mX) {\n var line = lineAtHeight(cm.doc, mY);\n var gutter = cm.display.gutterSpecs[i];\n signal(cm, type, cm, line, gutter.className, e);\n return e_defaultPrevented(e)\n }\n }\n }\n\n function clickInGutter(cm, e) {\n return gutterEvent(cm, e, \"gutterClick\", true)\n }\n\n // CONTEXT MENU HANDLING\n\n // To make the context menu work, we need to briefly unhide the\n // textarea (making it as unobtrusive as possible) to let the\n // right-click take effect on it.\n function onContextMenu(cm, e) {\n if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }\n if (signalDOMEvent(cm, e, \"contextmenu\")) { return }\n if (!captureRightClick) { cm.display.input.onContextMenu(e); }\n }\n\n function contextMenuInGutter(cm, e) {\n if (!hasHandler(cm, \"gutterContextMenu\")) { return false }\n return gutterEvent(cm, e, \"gutterContextMenu\", false)\n }\n\n function themeChanged(cm) {\n cm.display.wrapper.className = cm.display.wrapper.className.replace(/\\s*cm-s-\\S+/g, \"\") +\n cm.options.theme.replace(/(^|\\s)\\s*/g, \" cm-s-\");\n clearCaches(cm);\n }\n\n var Init = {toString: function(){return \"CodeMirror.Init\"}};\n\n var defaults = {};\n var optionHandlers = {};\n\n function defineOptions(CodeMirror) {\n var optionHandlers = CodeMirror.optionHandlers;\n\n function option(name, deflt, handle, notOnInit) {\n CodeMirror.defaults[name] = deflt;\n if (handle) { optionHandlers[name] =\n notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; }\n }\n\n CodeMirror.defineOption = option;\n\n // Passed to option handlers when there is no old value.\n CodeMirror.Init = Init;\n\n // These two are, on init, called from the constructor because they\n // have to be initialized before the editor can start at all.\n option(\"value\", \"\", function (cm, val) { return cm.setValue(val); }, true);\n option(\"mode\", null, function (cm, val) {\n cm.doc.modeOption = val;\n loadMode(cm);\n }, true);\n\n option(\"indentUnit\", 2, loadMode, true);\n option(\"indentWithTabs\", false);\n option(\"smartIndent\", true);\n option(\"tabSize\", 4, function (cm) {\n resetModeState(cm);\n clearCaches(cm);\n regChange(cm);\n }, true);\n\n option(\"lineSeparator\", null, function (cm, val) {\n cm.doc.lineSep = val;\n if (!val) { return }\n var newBreaks = [], lineNo = cm.doc.first;\n cm.doc.iter(function (line) {\n for (var pos = 0;;) {\n var found = line.text.indexOf(val, pos);\n if (found == -1) { break }\n pos = found + val.length;\n newBreaks.push(Pos(lineNo, found));\n }\n lineNo++;\n });\n for (var i = newBreaks.length - 1; i >= 0; i--)\n { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); }\n });\n option(\"specialChars\", /[\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u061c\\u200b-\\u200f\\u2028\\u2029\\ufeff\\ufff9-\\ufffc]/g, function (cm, val, old) {\n cm.state.specialChars = new RegExp(val.source + (val.test(\"\\t\") ? \"\" : \"|\\t\"), \"g\");\n if (old != Init) { cm.refresh(); }\n });\n option(\"specialCharPlaceholder\", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true);\n option(\"electricChars\", true);\n option(\"inputStyle\", mobile ? \"contenteditable\" : \"textarea\", function () {\n throw new Error(\"inputStyle can not (yet) be changed in a running editor\") // FIXME\n }, true);\n option(\"spellcheck\", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true);\n option(\"autocorrect\", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true);\n option(\"autocapitalize\", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true);\n option(\"rtlMoveVisually\", !windows);\n option(\"wholeLineUpdateBefore\", true);\n\n option(\"theme\", \"default\", function (cm) {\n themeChanged(cm);\n updateGutters(cm);\n }, true);\n option(\"keyMap\", \"default\", function (cm, val, old) {\n var next = getKeyMap(val);\n var prev = old != Init && getKeyMap(old);\n if (prev && prev.detach) { prev.detach(cm, next); }\n if (next.attach) { next.attach(cm, prev || null); }\n });\n option(\"extraKeys\", null);\n option(\"configureMouse\", null);\n\n option(\"lineWrapping\", false, wrappingChanged, true);\n option(\"gutters\", [], function (cm, val) {\n cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers);\n updateGutters(cm);\n }, true);\n option(\"fixedGutter\", true, function (cm, val) {\n cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + \"px\" : \"0\";\n cm.refresh();\n }, true);\n option(\"coverGutterNextToScrollbar\", false, function (cm) { return updateScrollbars(cm); }, true);\n option(\"scrollbarStyle\", \"native\", function (cm) {\n initScrollbars(cm);\n updateScrollbars(cm);\n cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);\n cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);\n }, true);\n option(\"lineNumbers\", false, function (cm, val) {\n cm.display.gutterSpecs = getGutters(cm.options.gutters, val);\n updateGutters(cm);\n }, true);\n option(\"firstLineNumber\", 1, updateGutters, true);\n option(\"lineNumberFormatter\", function (integer) { return integer; }, updateGutters, true);\n option(\"showCursorWhenSelecting\", false, updateSelection, true);\n\n option(\"resetSelectionOnContextMenu\", true);\n option(\"lineWiseCopyCut\", true);\n option(\"pasteLinesPerSelection\", true);\n option(\"selectionsMayTouch\", false);\n\n option(\"readOnly\", false, function (cm, val) {\n if (val == \"nocursor\") {\n onBlur(cm);\n cm.display.input.blur();\n }\n cm.display.input.readOnlyChanged(val);\n });\n option(\"disableInput\", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true);\n option(\"dragDrop\", true, dragDropChanged);\n option(\"allowDropFileTypes\", null);\n\n option(\"cursorBlinkRate\", 530);\n option(\"cursorScrollMargin\", 0);\n option(\"cursorHeight\", 1, updateSelection, true);\n option(\"singleCursorHeightPerLine\", true, updateSelection, true);\n option(\"workTime\", 100);\n option(\"workDelay\", 100);\n option(\"flattenSpans\", true, resetModeState, true);\n option(\"addModeClass\", false, resetModeState, true);\n option(\"pollInterval\", 100);\n option(\"undoDepth\", 200, function (cm, val) { return cm.doc.history.undoDepth = val; });\n option(\"historyEventDelay\", 1250);\n option(\"viewportMargin\", 10, function (cm) { return cm.refresh(); }, true);\n option(\"maxHighlightLength\", 10000, resetModeState, true);\n option(\"moveInputWithCursor\", true, function (cm, val) {\n if (!val) { cm.display.input.resetPosition(); }\n });\n\n option(\"tabindex\", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || \"\"; });\n option(\"autofocus\", null);\n option(\"direction\", \"ltr\", function (cm, val) { return cm.doc.setDirection(val); }, true);\n option(\"phrases\", null);\n }\n\n function dragDropChanged(cm, value, old) {\n var wasOn = old && old != Init;\n if (!value != !wasOn) {\n var funcs = cm.display.dragFunctions;\n var toggle = value ? on : off;\n toggle(cm.display.scroller, \"dragstart\", funcs.start);\n toggle(cm.display.scroller, \"dragenter\", funcs.enter);\n toggle(cm.display.scroller, \"dragover\", funcs.over);\n toggle(cm.display.scroller, \"dragleave\", funcs.leave);\n toggle(cm.display.scroller, \"drop\", funcs.drop);\n }\n }\n\n function wrappingChanged(cm) {\n if (cm.options.lineWrapping) {\n addClass(cm.display.wrapper, \"CodeMirror-wrap\");\n cm.display.sizer.style.minWidth = \"\";\n cm.display.sizerWidth = null;\n } else {\n rmClass(cm.display.wrapper, \"CodeMirror-wrap\");\n findMaxLine(cm);\n }\n estimateLineHeights(cm);\n regChange(cm);\n clearCaches(cm);\n setTimeout(function () { return updateScrollbars(cm); }, 100);\n }\n\n // A CodeMirror instance represents an editor. This is the object\n // that user code is usually dealing with.\n\n function CodeMirror(place, options) {\n var this$1 = this;\n\n if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }\n\n this.options = options = options ? copyObj(options) : {};\n // Determine effective options based on given values and defaults.\n copyObj(defaults, options, false);\n\n var doc = options.value;\n if (typeof doc == \"string\") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); }\n else if (options.mode) { doc.modeOption = options.mode; }\n this.doc = doc;\n\n var input = new CodeMirror.inputStyles[options.inputStyle](this);\n var display = this.display = new Display(place, doc, input, options);\n display.wrapper.CodeMirror = this;\n themeChanged(this);\n if (options.lineWrapping)\n { this.display.wrapper.className += \" CodeMirror-wrap\"; }\n initScrollbars(this);\n\n this.state = {\n keyMaps: [], // stores maps added by addKeyMap\n overlays: [], // highlighting overlays, as added by addOverlay\n modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info\n overwrite: false,\n delayingBlurEvent: false,\n focused: false,\n suppressEdits: false, // used to disable editing during key handlers when in readOnly mode\n pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll\n selectingText: false,\n draggingText: false,\n highlight: new Delayed(), // stores highlight worker timeout\n keySeq: null, // Unfinished key sequence\n specialChars: null\n };\n\n if (options.autofocus && !mobile) { display.input.focus(); }\n\n // Override magic textarea content restore that IE sometimes does\n // on our hidden textarea on reload\n if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); }\n\n registerEventHandlers(this);\n ensureGlobalHandlers();\n\n startOperation(this);\n this.curOp.forceUpdate = true;\n attachDoc(this, doc);\n\n if ((options.autofocus && !mobile) || this.hasFocus())\n { setTimeout(bind(onFocus, this), 20); }\n else\n { onBlur(this); }\n\n for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))\n { optionHandlers[opt](this, options[opt], Init); } }\n maybeUpdateLineNumberWidth(this);\n if (options.finishInit) { options.finishInit(this); }\n for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this); }\n endOperation(this);\n // Suppress optimizelegibility in Webkit, since it breaks text\n // measuring on line wrapping boundaries.\n if (webkit && options.lineWrapping &&\n getComputedStyle(display.lineDiv).textRendering == \"optimizelegibility\")\n { display.lineDiv.style.textRendering = \"auto\"; }\n }\n\n // The default configuration options.\n CodeMirror.defaults = defaults;\n // Functions to run when options are changed.\n CodeMirror.optionHandlers = optionHandlers;\n\n // Attach the necessary event handlers when initializing the editor\n function registerEventHandlers(cm) {\n var d = cm.display;\n on(d.scroller, \"mousedown\", operation(cm, onMouseDown));\n // Older IE's will not fire a second mousedown for a double click\n if (ie && ie_version < 11)\n { on(d.scroller, \"dblclick\", operation(cm, function (e) {\n if (signalDOMEvent(cm, e)) { return }\n var pos = posFromMouse(cm, e);\n if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }\n e_preventDefault(e);\n var word = cm.findWordAt(pos);\n extendSelection(cm.doc, word.anchor, word.head);\n })); }\n else\n { on(d.scroller, \"dblclick\", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); }\n // Some browsers fire contextmenu *after* opening the menu, at\n // which point we can't mess with it anymore. Context menu is\n // handled in onMouseDown for these browsers.\n on(d.scroller, \"contextmenu\", function (e) { return onContextMenu(cm, e); });\n on(d.input.getField(), \"contextmenu\", function (e) {\n if (!d.scroller.contains(e.target)) { onContextMenu(cm, e); }\n });\n\n // Used to suppress mouse event handling when a touch happens\n var touchFinished, prevTouch = {end: 0};\n function finishTouch() {\n if (d.activeTouch) {\n touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000);\n prevTouch = d.activeTouch;\n prevTouch.end = +new Date;\n }\n }\n function isMouseLikeTouchEvent(e) {\n if (e.touches.length != 1) { return false }\n var touch = e.touches[0];\n return touch.radiusX <= 1 && touch.radiusY <= 1\n }\n function farAway(touch, other) {\n if (other.left == null) { return true }\n var dx = other.left - touch.left, dy = other.top - touch.top;\n return dx * dx + dy * dy > 20 * 20\n }\n on(d.scroller, \"touchstart\", function (e) {\n if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {\n d.input.ensurePolled();\n clearTimeout(touchFinished);\n var now = +new Date;\n d.activeTouch = {start: now, moved: false,\n prev: now - prevTouch.end <= 300 ? prevTouch : null};\n if (e.touches.length == 1) {\n d.activeTouch.left = e.touches[0].pageX;\n d.activeTouch.top = e.touches[0].pageY;\n }\n }\n });\n on(d.scroller, \"touchmove\", function () {\n if (d.activeTouch) { d.activeTouch.moved = true; }\n });\n on(d.scroller, \"touchend\", function (e) {\n var touch = d.activeTouch;\n if (touch && !eventInWidget(d, e) && touch.left != null &&\n !touch.moved && new Date - touch.start < 300) {\n var pos = cm.coordsChar(d.activeTouch, \"page\"), range;\n if (!touch.prev || farAway(touch, touch.prev)) // Single tap\n { range = new Range(pos, pos); }\n else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap\n { range = cm.findWordAt(pos); }\n else // Triple tap\n { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); }\n cm.setSelection(range.anchor, range.head);\n cm.focus();\n e_preventDefault(e);\n }\n finishTouch();\n });\n on(d.scroller, \"touchcancel\", finishTouch);\n\n // Sync scrolling between fake scrollbars and real scrollable\n // area, ensure viewport is updated when scrolling.\n on(d.scroller, \"scroll\", function () {\n if (d.scroller.clientHeight) {\n updateScrollTop(cm, d.scroller.scrollTop);\n setScrollLeft(cm, d.scroller.scrollLeft, true);\n signal(cm, \"scroll\", cm);\n }\n });\n\n // Listen to wheel events in order to try and update the viewport on time.\n on(d.scroller, \"mousewheel\", function (e) { return onScrollWheel(cm, e); });\n on(d.scroller, \"DOMMouseScroll\", function (e) { return onScrollWheel(cm, e); });\n\n // Prevent wrapper from ever scrolling\n on(d.wrapper, \"scroll\", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });\n\n d.dragFunctions = {\n enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }},\n over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},\n start: function (e) { return onDragStart(cm, e); },\n drop: operation(cm, onDrop),\n leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}\n };\n\n var inp = d.input.getField();\n on(inp, \"keyup\", function (e) { return onKeyUp.call(cm, e); });\n on(inp, \"keydown\", operation(cm, onKeyDown));\n on(inp, \"keypress\", operation(cm, onKeyPress));\n on(inp, \"focus\", function (e) { return onFocus(cm, e); });\n on(inp, \"blur\", function (e) { return onBlur(cm, e); });\n }\n\n var initHooks = [];\n CodeMirror.defineInitHook = function (f) { return initHooks.push(f); };\n\n // Indent the given line. The how parameter can be \"smart\",\n // \"add\"/null, \"subtract\", or \"prev\". When aggressive is false\n // (typically set to true for forced single-line indents), empty\n // lines are not indented, and places where the mode returns Pass\n // are left alone.\n function indentLine(cm, n, how, aggressive) {\n var doc = cm.doc, state;\n if (how == null) { how = \"add\"; }\n if (how == \"smart\") {\n // Fall back to \"prev\" when the mode doesn't have an indentation\n // method.\n if (!doc.mode.indent) { how = \"prev\"; }\n else { state = getContextBefore(cm, n).state; }\n }\n\n var tabSize = cm.options.tabSize;\n var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);\n if (line.stateAfter) { line.stateAfter = null; }\n var curSpaceString = line.text.match(/^\\s*/)[0], indentation;\n if (!aggressive && !/\\S/.test(line.text)) {\n indentation = 0;\n how = \"not\";\n } else if (how == \"smart\") {\n indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);\n if (indentation == Pass || indentation > 150) {\n if (!aggressive) { return }\n how = \"prev\";\n }\n }\n if (how == \"prev\") {\n if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); }\n else { indentation = 0; }\n } else if (how == \"add\") {\n indentation = curSpace + cm.options.indentUnit;\n } else if (how == \"subtract\") {\n indentation = curSpace - cm.options.indentUnit;\n } else if (typeof how == \"number\") {\n indentation = curSpace + how;\n }\n indentation = Math.max(0, indentation);\n\n var indentString = \"\", pos = 0;\n if (cm.options.indentWithTabs)\n { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += \"\\t\";} }\n if (pos < indentation) { indentString += spaceStr(indentation - pos); }\n\n if (indentString != curSpaceString) {\n replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), \"+input\");\n line.stateAfter = null;\n return true\n } else {\n // Ensure that, if the cursor was in the whitespace at the start\n // of the line, it is moved to the end of that space.\n for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {\n var range = doc.sel.ranges[i$1];\n if (range.head.line == n && range.head.ch < curSpaceString.length) {\n var pos$1 = Pos(n, curSpaceString.length);\n replaceOneSelection(doc, i$1, new Range(pos$1, pos$1));\n break\n }\n }\n }\n }\n\n // This will be set to a {lineWise: bool, text: [string]} object, so\n // that, when pasting, we know what kind of selections the copied\n // text was made out of.\n var lastCopied = null;\n\n function setLastCopied(newLastCopied) {\n lastCopied = newLastCopied;\n }\n\n function applyTextInput(cm, inserted, deleted, sel, origin) {\n var doc = cm.doc;\n cm.display.shift = false;\n if (!sel) { sel = doc.sel; }\n\n var recent = +new Date - 200;\n var paste = origin == \"paste\" || cm.state.pasteIncoming > recent;\n var textLines = splitLinesAuto(inserted), multiPaste = null;\n // When pasting N lines into N selections, insert one line per selection\n if (paste && sel.ranges.length > 1) {\n if (lastCopied && lastCopied.text.join(\"\\n\") == inserted) {\n if (sel.ranges.length % lastCopied.text.length == 0) {\n multiPaste = [];\n for (var i = 0; i < lastCopied.text.length; i++)\n { multiPaste.push(doc.splitLines(lastCopied.text[i])); }\n }\n } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {\n multiPaste = map(textLines, function (l) { return [l]; });\n }\n }\n\n var updateInput = cm.curOp.updateInput;\n // Normal behavior is to insert the new text into every selection\n for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {\n var range = sel.ranges[i$1];\n var from = range.from(), to = range.to();\n if (range.empty()) {\n if (deleted && deleted > 0) // Handle deletion\n { from = Pos(from.line, from.ch - deleted); }\n else if (cm.state.overwrite && !paste) // Handle overwrite\n { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); }\n else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join(\"\\n\") == inserted)\n { from = to = Pos(from.line, 0); }\n }\n var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,\n origin: origin || (paste ? \"paste\" : cm.state.cutIncoming > recent ? \"cut\" : \"+input\")};\n makeChange(cm.doc, changeEvent);\n signalLater(cm, \"inputRead\", cm, changeEvent);\n }\n if (inserted && !paste)\n { triggerElectric(cm, inserted); }\n\n ensureCursorVisible(cm);\n if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; }\n cm.curOp.typing = true;\n cm.state.pasteIncoming = cm.state.cutIncoming = -1;\n }\n\n function handlePaste(e, cm) {\n var pasted = e.clipboardData && e.clipboardData.getData(\"Text\");\n if (pasted) {\n e.preventDefault();\n if (!cm.isReadOnly() && !cm.options.disableInput)\n { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, \"paste\"); }); }\n return true\n }\n }\n\n function triggerElectric(cm, inserted) {\n // When an 'electric' character is inserted, immediately trigger a reindent\n if (!cm.options.electricChars || !cm.options.smartIndent) { return }\n var sel = cm.doc.sel;\n\n for (var i = sel.ranges.length - 1; i >= 0; i--) {\n var range = sel.ranges[i];\n if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue }\n var mode = cm.getModeAt(range.head);\n var indented = false;\n if (mode.electricChars) {\n for (var j = 0; j < mode.electricChars.length; j++)\n { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {\n indented = indentLine(cm, range.head.line, \"smart\");\n break\n } }\n } else if (mode.electricInput) {\n if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))\n { indented = indentLine(cm, range.head.line, \"smart\"); }\n }\n if (indented) { signalLater(cm, \"electricInput\", cm, range.head.line); }\n }\n }\n\n function copyableRanges(cm) {\n var text = [], ranges = [];\n for (var i = 0; i < cm.doc.sel.ranges.length; i++) {\n var line = cm.doc.sel.ranges[i].head.line;\n var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};\n ranges.push(lineRange);\n text.push(cm.getRange(lineRange.anchor, lineRange.head));\n }\n return {text: text, ranges: ranges}\n }\n\n function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) {\n field.setAttribute(\"autocorrect\", autocorrect ? \"\" : \"off\");\n field.setAttribute(\"autocapitalize\", autocapitalize ? \"\" : \"off\");\n field.setAttribute(\"spellcheck\", !!spellcheck);\n }\n\n function hiddenTextarea() {\n var te = elt(\"textarea\", null, null, \"position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none\");\n var div = elt(\"div\", [te], null, \"overflow: hidden; position: relative; width: 3px; height: 0px;\");\n // The textarea is kept positioned near the cursor to prevent the\n // fact that it'll be scrolled into view on input from scrolling\n // our fake cursor out of view. On webkit, when wrap=off, paste is\n // very slow. So make the area wide instead.\n if (webkit) { te.style.width = \"1000px\"; }\n else { te.setAttribute(\"wrap\", \"off\"); }\n // If border: 0; -- iOS fails to open keyboard (issue #1287)\n if (ios) { te.style.border = \"1px solid black\"; }\n disableBrowserMagic(te);\n return div\n }\n\n // The publicly visible API. Note that methodOp(f) means\n // 'wrap f in an operation, performed on its `this` parameter'.\n\n // This is not the complete set of editor methods. Most of the\n // methods defined on the Doc type are also injected into\n // CodeMirror.prototype, for backwards compatibility and\n // convenience.\n\n function addEditorMethods(CodeMirror) {\n var optionHandlers = CodeMirror.optionHandlers;\n\n var helpers = CodeMirror.helpers = {};\n\n CodeMirror.prototype = {\n constructor: CodeMirror,\n focus: function(){window.focus(); this.display.input.focus();},\n\n setOption: function(option, value) {\n var options = this.options, old = options[option];\n if (options[option] == value && option != \"mode\") { return }\n options[option] = value;\n if (optionHandlers.hasOwnProperty(option))\n { operation(this, optionHandlers[option])(this, value, old); }\n signal(this, \"optionChange\", this, option);\n },\n\n getOption: function(option) {return this.options[option]},\n getDoc: function() {return this.doc},\n\n addKeyMap: function(map, bottom) {\n this.state.keyMaps[bottom ? \"push\" : \"unshift\"](getKeyMap(map));\n },\n removeKeyMap: function(map) {\n var maps = this.state.keyMaps;\n for (var i = 0; i < maps.length; ++i)\n { if (maps[i] == map || maps[i].name == map) {\n maps.splice(i, 1);\n return true\n } }\n },\n\n addOverlay: methodOp(function(spec, options) {\n var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);\n if (mode.startState) { throw new Error(\"Overlays may not be stateful.\") }\n insertSorted(this.state.overlays,\n {mode: mode, modeSpec: spec, opaque: options && options.opaque,\n priority: (options && options.priority) || 0},\n function (overlay) { return overlay.priority; });\n this.state.modeGen++;\n regChange(this);\n }),\n removeOverlay: methodOp(function(spec) {\n var overlays = this.state.overlays;\n for (var i = 0; i < overlays.length; ++i) {\n var cur = overlays[i].modeSpec;\n if (cur == spec || typeof spec == \"string\" && cur.name == spec) {\n overlays.splice(i, 1);\n this.state.modeGen++;\n regChange(this);\n return\n }\n }\n }),\n\n indentLine: methodOp(function(n, dir, aggressive) {\n if (typeof dir != \"string\" && typeof dir != \"number\") {\n if (dir == null) { dir = this.options.smartIndent ? \"smart\" : \"prev\"; }\n else { dir = dir ? \"add\" : \"subtract\"; }\n }\n if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); }\n }),\n indentSelection: methodOp(function(how) {\n var ranges = this.doc.sel.ranges, end = -1;\n for (var i = 0; i < ranges.length; i++) {\n var range = ranges[i];\n if (!range.empty()) {\n var from = range.from(), to = range.to();\n var start = Math.max(end, from.line);\n end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;\n for (var j = start; j < end; ++j)\n { indentLine(this, j, how); }\n var newRanges = this.doc.sel.ranges;\n if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)\n { replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); }\n } else if (range.head.line > end) {\n indentLine(this, range.head.line, how, true);\n end = range.head.line;\n if (i == this.doc.sel.primIndex) { ensureCursorVisible(this); }\n }\n }\n }),\n\n // Fetch the parser token for a given character. Useful for hacks\n // that want to inspect the mode state (say, for completion).\n getTokenAt: function(pos, precise) {\n return takeToken(this, pos, precise)\n },\n\n getLineTokens: function(line, precise) {\n return takeToken(this, Pos(line), precise, true)\n },\n\n getTokenTypeAt: function(pos) {\n pos = clipPos(this.doc, pos);\n var styles = getLineStyles(this, getLine(this.doc, pos.line));\n var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;\n var type;\n if (ch == 0) { type = styles[2]; }\n else { for (;;) {\n var mid = (before + after) >> 1;\n if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; }\n else if (styles[mid * 2 + 1] < ch) { before = mid + 1; }\n else { type = styles[mid * 2 + 2]; break }\n } }\n var cut = type ? type.indexOf(\"overlay \") : -1;\n return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)\n },\n\n getModeAt: function(pos) {\n var mode = this.doc.mode;\n if (!mode.innerMode) { return mode }\n return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode\n },\n\n getHelper: function(pos, type) {\n return this.getHelpers(pos, type)[0]\n },\n\n getHelpers: function(pos, type) {\n var found = [];\n if (!helpers.hasOwnProperty(type)) { return found }\n var help = helpers[type], mode = this.getModeAt(pos);\n if (typeof mode[type] == \"string\") {\n if (help[mode[type]]) { found.push(help[mode[type]]); }\n } else if (mode[type]) {\n for (var i = 0; i < mode[type].length; i++) {\n var val = help[mode[type][i]];\n if (val) { found.push(val); }\n }\n } else if (mode.helperType && help[mode.helperType]) {\n found.push(help[mode.helperType]);\n } else if (help[mode.name]) {\n found.push(help[mode.name]);\n }\n for (var i$1 = 0; i$1 < help._global.length; i$1++) {\n var cur = help._global[i$1];\n if (cur.pred(mode, this) && indexOf(found, cur.val) == -1)\n { found.push(cur.val); }\n }\n return found\n },\n\n getStateAfter: function(line, precise) {\n var doc = this.doc;\n line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);\n return getContextBefore(this, line + 1, precise).state\n },\n\n cursorCoords: function(start, mode) {\n var pos, range = this.doc.sel.primary();\n if (start == null) { pos = range.head; }\n else if (typeof start == \"object\") { pos = clipPos(this.doc, start); }\n else { pos = start ? range.from() : range.to(); }\n return cursorCoords(this, pos, mode || \"page\")\n },\n\n charCoords: function(pos, mode) {\n return charCoords(this, clipPos(this.doc, pos), mode || \"page\")\n },\n\n coordsChar: function(coords, mode) {\n coords = fromCoordSystem(this, coords, mode || \"page\");\n return coordsChar(this, coords.left, coords.top)\n },\n\n lineAtHeight: function(height, mode) {\n height = fromCoordSystem(this, {top: height, left: 0}, mode || \"page\").top;\n return lineAtHeight(this.doc, height + this.display.viewOffset)\n },\n heightAtLine: function(line, mode, includeWidgets) {\n var end = false, lineObj;\n if (typeof line == \"number\") {\n var last = this.doc.first + this.doc.size - 1;\n if (line < this.doc.first) { line = this.doc.first; }\n else if (line > last) { line = last; end = true; }\n lineObj = getLine(this.doc, line);\n } else {\n lineObj = line;\n }\n return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || \"page\", includeWidgets || end).top +\n (end ? this.doc.height - heightAtLine(lineObj) : 0)\n },\n\n defaultTextHeight: function() { return textHeight(this.display) },\n defaultCharWidth: function() { return charWidth(this.display) },\n\n getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},\n\n addWidget: function(pos, node, scroll, vert, horiz) {\n var display = this.display;\n pos = cursorCoords(this, clipPos(this.doc, pos));\n var top = pos.bottom, left = pos.left;\n node.style.position = \"absolute\";\n node.setAttribute(\"cm-ignore-events\", \"true\");\n this.display.input.setUneditable(node);\n display.sizer.appendChild(node);\n if (vert == \"over\") {\n top = pos.top;\n } else if (vert == \"above\" || vert == \"near\") {\n var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),\n hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);\n // Default to positioning above (if specified and possible); otherwise default to positioning below\n if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)\n { top = pos.top - node.offsetHeight; }\n else if (pos.bottom + node.offsetHeight <= vspace)\n { top = pos.bottom; }\n if (left + node.offsetWidth > hspace)\n { left = hspace - node.offsetWidth; }\n }\n node.style.top = top + \"px\";\n node.style.left = node.style.right = \"\";\n if (horiz == \"right\") {\n left = display.sizer.clientWidth - node.offsetWidth;\n node.style.right = \"0px\";\n } else {\n if (horiz == \"left\") { left = 0; }\n else if (horiz == \"middle\") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; }\n node.style.left = left + \"px\";\n }\n if (scroll)\n { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); }\n },\n\n triggerOnKeyDown: methodOp(onKeyDown),\n triggerOnKeyPress: methodOp(onKeyPress),\n triggerOnKeyUp: onKeyUp,\n triggerOnMouseDown: methodOp(onMouseDown),\n\n execCommand: function(cmd) {\n if (commands.hasOwnProperty(cmd))\n { return commands[cmd].call(null, this) }\n },\n\n triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),\n\n findPosH: function(from, amount, unit, visually) {\n var dir = 1;\n if (amount < 0) { dir = -1; amount = -amount; }\n var cur = clipPos(this.doc, from);\n for (var i = 0; i < amount; ++i) {\n cur = findPosH(this.doc, cur, dir, unit, visually);\n if (cur.hitSide) { break }\n }\n return cur\n },\n\n moveH: methodOp(function(dir, unit) {\n var this$1 = this;\n\n this.extendSelectionsBy(function (range) {\n if (this$1.display.shift || this$1.doc.extend || range.empty())\n { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) }\n else\n { return dir < 0 ? range.from() : range.to() }\n }, sel_move);\n }),\n\n deleteH: methodOp(function(dir, unit) {\n var sel = this.doc.sel, doc = this.doc;\n if (sel.somethingSelected())\n { doc.replaceSelection(\"\", null, \"+delete\"); }\n else\n { deleteNearSelection(this, function (range) {\n var other = findPosH(doc, range.head, dir, unit, false);\n return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other}\n }); }\n }),\n\n findPosV: function(from, amount, unit, goalColumn) {\n var dir = 1, x = goalColumn;\n if (amount < 0) { dir = -1; amount = -amount; }\n var cur = clipPos(this.doc, from);\n for (var i = 0; i < amount; ++i) {\n var coords = cursorCoords(this, cur, \"div\");\n if (x == null) { x = coords.left; }\n else { coords.left = x; }\n cur = findPosV(this, coords, dir, unit);\n if (cur.hitSide) { break }\n }\n return cur\n },\n\n moveV: methodOp(function(dir, unit) {\n var this$1 = this;\n\n var doc = this.doc, goals = [];\n var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected();\n doc.extendSelectionsBy(function (range) {\n if (collapse)\n { return dir < 0 ? range.from() : range.to() }\n var headPos = cursorCoords(this$1, range.head, \"div\");\n if (range.goalColumn != null) { headPos.left = range.goalColumn; }\n goals.push(headPos.left);\n var pos = findPosV(this$1, headPos, dir, unit);\n if (unit == \"page\" && range == doc.sel.primary())\n { addToScrollTop(this$1, charCoords(this$1, pos, \"div\").top - headPos.top); }\n return pos\n }, sel_move);\n if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)\n { doc.sel.ranges[i].goalColumn = goals[i]; } }\n }),\n\n // Find the word at the given position (as returned by coordsChar).\n findWordAt: function(pos) {\n var doc = this.doc, line = getLine(doc, pos.line).text;\n var start = pos.ch, end = pos.ch;\n if (line) {\n var helper = this.getHelper(pos, \"wordChars\");\n if ((pos.sticky == \"before\" || end == line.length) && start) { --start; } else { ++end; }\n var startChar = line.charAt(start);\n var check = isWordChar(startChar, helper)\n ? function (ch) { return isWordChar(ch, helper); }\n : /\\s/.test(startChar) ? function (ch) { return /\\s/.test(ch); }\n : function (ch) { return (!/\\s/.test(ch) && !isWordChar(ch)); };\n while (start > 0 && check(line.charAt(start - 1))) { --start; }\n while (end < line.length && check(line.charAt(end))) { ++end; }\n }\n return new Range(Pos(pos.line, start), Pos(pos.line, end))\n },\n\n toggleOverwrite: function(value) {\n if (value != null && value == this.state.overwrite) { return }\n if (this.state.overwrite = !this.state.overwrite)\n { addClass(this.display.cursorDiv, \"CodeMirror-overwrite\"); }\n else\n { rmClass(this.display.cursorDiv, \"CodeMirror-overwrite\"); }\n\n signal(this, \"overwriteToggle\", this, this.state.overwrite);\n },\n hasFocus: function() { return this.display.input.getField() == activeElt() },\n isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },\n\n scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }),\n getScrollInfo: function() {\n var scroller = this.display.scroller;\n return {left: scroller.scrollLeft, top: scroller.scrollTop,\n height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,\n width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,\n clientHeight: displayHeight(this), clientWidth: displayWidth(this)}\n },\n\n scrollIntoView: methodOp(function(range, margin) {\n if (range == null) {\n range = {from: this.doc.sel.primary().head, to: null};\n if (margin == null) { margin = this.options.cursorScrollMargin; }\n } else if (typeof range == \"number\") {\n range = {from: Pos(range, 0), to: null};\n } else if (range.from == null) {\n range = {from: range, to: null};\n }\n if (!range.to) { range.to = range.from; }\n range.margin = margin || 0;\n\n if (range.from.line != null) {\n scrollToRange(this, range);\n } else {\n scrollToCoordsRange(this, range.from, range.to, range.margin);\n }\n }),\n\n setSize: methodOp(function(width, height) {\n var this$1 = this;\n\n var interpret = function (val) { return typeof val == \"number\" || /^\\d+$/.test(String(val)) ? val + \"px\" : val; };\n if (width != null) { this.display.wrapper.style.width = interpret(width); }\n if (height != null) { this.display.wrapper.style.height = interpret(height); }\n if (this.options.lineWrapping) { clearLineMeasurementCache(this); }\n var lineNo = this.display.viewFrom;\n this.doc.iter(lineNo, this.display.viewTo, function (line) {\n if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)\n { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, \"widget\"); break } } }\n ++lineNo;\n });\n this.curOp.forceUpdate = true;\n signal(this, \"refresh\", this);\n }),\n\n operation: function(f){return runInOp(this, f)},\n startOperation: function(){return startOperation(this)},\n endOperation: function(){return endOperation(this)},\n\n refresh: methodOp(function() {\n var oldHeight = this.display.cachedTextHeight;\n regChange(this);\n this.curOp.forceUpdate = true;\n clearCaches(this);\n scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);\n updateGutterSpace(this.display);\n if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)\n { estimateLineHeights(this); }\n signal(this, \"refresh\", this);\n }),\n\n swapDoc: methodOp(function(doc) {\n var old = this.doc;\n old.cm = null;\n // Cancel the current text selection if any (#5821)\n if (this.state.selectingText) { this.state.selectingText(); }\n attachDoc(this, doc);\n clearCaches(this);\n this.display.input.reset();\n scrollToCoords(this, doc.scrollLeft, doc.scrollTop);\n this.curOp.forceScroll = true;\n signalLater(this, \"swapDoc\", this, old);\n return old\n }),\n\n phrase: function(phraseText) {\n var phrases = this.options.phrases;\n return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText\n },\n\n getInputField: function(){return this.display.input.getField()},\n getWrapperElement: function(){return this.display.wrapper},\n getScrollerElement: function(){return this.display.scroller},\n getGutterElement: function(){return this.display.gutters}\n };\n eventMixin(CodeMirror);\n\n CodeMirror.registerHelper = function(type, name, value) {\n if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; }\n helpers[type][name] = value;\n };\n CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {\n CodeMirror.registerHelper(type, name, value);\n helpers[type]._global.push({pred: predicate, val: value});\n };\n }\n\n // Used for horizontal relative motion. Dir is -1 or 1 (left or\n // right), unit can be \"char\", \"column\" (like char, but doesn't\n // cross line boundaries), \"word\" (across next word), or \"group\" (to\n // the start of next group of word or non-word-non-whitespace\n // chars). The visually param controls whether, in right-to-left\n // text, direction 1 means to move towards the next index in the\n // string, or towards the character to the right of the current\n // position. The resulting position will have a hitSide=true\n // property if it reached the end of the document.\n function findPosH(doc, pos, dir, unit, visually) {\n var oldPos = pos;\n var origDir = dir;\n var lineObj = getLine(doc, pos.line);\n var lineDir = visually && doc.cm && doc.cm.getOption(\"direction\") == \"rtl\" ? -dir : dir;\n function findNextLine() {\n var l = pos.line + lineDir;\n if (l < doc.first || l >= doc.first + doc.size) { return false }\n pos = new Pos(l, pos.ch, pos.sticky);\n return lineObj = getLine(doc, l)\n }\n function moveOnce(boundToLine) {\n var next;\n if (visually) {\n next = moveVisually(doc.cm, lineObj, pos, dir);\n } else {\n next = moveLogically(lineObj, pos, dir);\n }\n if (next == null) {\n if (!boundToLine && findNextLine())\n { pos = endOfLine(visually, doc.cm, lineObj, pos.line, lineDir); }\n else\n { return false }\n } else {\n pos = next;\n }\n return true\n }\n\n if (unit == \"char\") {\n moveOnce();\n } else if (unit == \"column\") {\n moveOnce(true);\n } else if (unit == \"word\" || unit == \"group\") {\n var sawType = null, group = unit == \"group\";\n var helper = doc.cm && doc.cm.getHelper(pos, \"wordChars\");\n for (var first = true;; first = false) {\n if (dir < 0 && !moveOnce(!first)) { break }\n var cur = lineObj.text.charAt(pos.ch) || \"\\n\";\n var type = isWordChar(cur, helper) ? \"w\"\n : group && cur == \"\\n\" ? \"n\"\n : !group || /\\s/.test(cur) ? null\n : \"p\";\n if (group && !first && !type) { type = \"s\"; }\n if (sawType && sawType != type) {\n if (dir < 0) {dir = 1; moveOnce(); pos.sticky = \"after\";}\n break\n }\n\n if (type) { sawType = type; }\n if (dir > 0 && !moveOnce(!first)) { break }\n }\n }\n var result = skipAtomic(doc, pos, oldPos, origDir, true);\n if (equalCursorPos(oldPos, result)) { result.hitSide = true; }\n return result\n }\n\n // For relative vertical movement. Dir may be -1 or 1. Unit can be\n // \"page\" or \"line\". The resulting position will have a hitSide=true\n // property if it reached the end of the document.\n function findPosV(cm, pos, dir, unit) {\n var doc = cm.doc, x = pos.left, y;\n if (unit == \"page\") {\n var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);\n var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3);\n y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount;\n\n } else if (unit == \"line\") {\n y = dir > 0 ? pos.bottom + 3 : pos.top - 3;\n }\n var target;\n for (;;) {\n target = coordsChar(cm, x, y);\n if (!target.outside) { break }\n if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }\n y += dir * 5;\n }\n return target\n }\n\n // CONTENTEDITABLE INPUT STYLE\n\n var ContentEditableInput = function(cm) {\n this.cm = cm;\n this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;\n this.polling = new Delayed();\n this.composing = null;\n this.gracePeriod = false;\n this.readDOMTimeout = null;\n };\n\n ContentEditableInput.prototype.init = function (display) {\n var this$1 = this;\n\n var input = this, cm = input.cm;\n var div = input.div = display.lineDiv;\n disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize);\n\n on(div, \"paste\", function (e) {\n if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }\n // IE doesn't fire input events, so we schedule a read for the pasted content in this way\n if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }\n });\n\n on(div, \"compositionstart\", function (e) {\n this$1.composing = {data: e.data, done: false};\n });\n on(div, \"compositionupdate\", function (e) {\n if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; }\n });\n on(div, \"compositionend\", function (e) {\n if (this$1.composing) {\n if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); }\n this$1.composing.done = true;\n }\n });\n\n on(div, \"touchstart\", function () { return input.forceCompositionEnd(); });\n\n on(div, \"input\", function () {\n if (!this$1.composing) { this$1.readFromDOMSoon(); }\n });\n\n function onCopyCut(e) {\n if (signalDOMEvent(cm, e)) { return }\n if (cm.somethingSelected()) {\n setLastCopied({lineWise: false, text: cm.getSelections()});\n if (e.type == \"cut\") { cm.replaceSelection(\"\", null, \"cut\"); }\n } else if (!cm.options.lineWiseCopyCut) {\n return\n } else {\n var ranges = copyableRanges(cm);\n setLastCopied({lineWise: true, text: ranges.text});\n if (e.type == \"cut\") {\n cm.operation(function () {\n cm.setSelections(ranges.ranges, 0, sel_dontScroll);\n cm.replaceSelection(\"\", null, \"cut\");\n });\n }\n }\n if (e.clipboardData) {\n e.clipboardData.clearData();\n var content = lastCopied.text.join(\"\\n\");\n // iOS exposes the clipboard API, but seems to discard content inserted into it\n e.clipboardData.setData(\"Text\", content);\n if (e.clipboardData.getData(\"Text\") == content) {\n e.preventDefault();\n return\n }\n }\n // Old-fashioned briefly-focus-a-textarea hack\n var kludge = hiddenTextarea(), te = kludge.firstChild;\n cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);\n te.value = lastCopied.text.join(\"\\n\");\n var hadFocus = document.activeElement;\n selectInput(te);\n setTimeout(function () {\n cm.display.lineSpace.removeChild(kludge);\n hadFocus.focus();\n if (hadFocus == div) { input.showPrimarySelection(); }\n }, 50);\n }\n on(div, \"copy\", onCopyCut);\n on(div, \"cut\", onCopyCut);\n };\n\n ContentEditableInput.prototype.prepareSelection = function () {\n var result = prepareSelection(this.cm, false);\n result.focus = this.cm.state.focused;\n return result\n };\n\n ContentEditableInput.prototype.showSelection = function (info, takeFocus) {\n if (!info || !this.cm.display.view.length) { return }\n if (info.focus || takeFocus) { this.showPrimarySelection(); }\n this.showMultipleSelections(info);\n };\n\n ContentEditableInput.prototype.getSelection = function () {\n return this.cm.display.wrapper.ownerDocument.getSelection()\n };\n\n ContentEditableInput.prototype.showPrimarySelection = function () {\n var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();\n var from = prim.from(), to = prim.to();\n\n if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {\n sel.removeAllRanges();\n return\n }\n\n var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);\n var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset);\n if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&\n cmp(minPos(curAnchor, curFocus), from) == 0 &&\n cmp(maxPos(curAnchor, curFocus), to) == 0)\n { return }\n\n var view = cm.display.view;\n var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||\n {node: view[0].measure.map[2], offset: 0};\n var end = to.line < cm.display.viewTo && posToDOM(cm, to);\n if (!end) {\n var measure = view[view.length - 1].measure;\n var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;\n end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]};\n }\n\n if (!start || !end) {\n sel.removeAllRanges();\n return\n }\n\n var old = sel.rangeCount && sel.getRangeAt(0), rng;\n try { rng = range(start.node, start.offset, end.offset, end.node); }\n catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible\n if (rng) {\n if (!gecko && cm.state.focused) {\n sel.collapse(start.node, start.offset);\n if (!rng.collapsed) {\n sel.removeAllRanges();\n sel.addRange(rng);\n }\n } else {\n sel.removeAllRanges();\n sel.addRange(rng);\n }\n if (old && sel.anchorNode == null) { sel.addRange(old); }\n else if (gecko) { this.startGracePeriod(); }\n }\n this.rememberSelection();\n };\n\n ContentEditableInput.prototype.startGracePeriod = function () {\n var this$1 = this;\n\n clearTimeout(this.gracePeriod);\n this.gracePeriod = setTimeout(function () {\n this$1.gracePeriod = false;\n if (this$1.selectionChanged())\n { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); }\n }, 20);\n };\n\n ContentEditableInput.prototype.showMultipleSelections = function (info) {\n removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);\n removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);\n };\n\n ContentEditableInput.prototype.rememberSelection = function () {\n var sel = this.getSelection();\n this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;\n this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;\n };\n\n ContentEditableInput.prototype.selectionInEditor = function () {\n var sel = this.getSelection();\n if (!sel.rangeCount) { return false }\n var node = sel.getRangeAt(0).commonAncestorContainer;\n return contains(this.div, node)\n };\n\n ContentEditableInput.prototype.focus = function () {\n if (this.cm.options.readOnly != \"nocursor\") {\n if (!this.selectionInEditor())\n { this.showSelection(this.prepareSelection(), true); }\n this.div.focus();\n }\n };\n ContentEditableInput.prototype.blur = function () { this.div.blur(); };\n ContentEditableInput.prototype.getField = function () { return this.div };\n\n ContentEditableInput.prototype.supportsTouch = function () { return true };\n\n ContentEditableInput.prototype.receivedFocus = function () {\n var input = this;\n if (this.selectionInEditor())\n { this.pollSelection(); }\n else\n { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); }\n\n function poll() {\n if (input.cm.state.focused) {\n input.pollSelection();\n input.polling.set(input.cm.options.pollInterval, poll);\n }\n }\n this.polling.set(this.cm.options.pollInterval, poll);\n };\n\n ContentEditableInput.prototype.selectionChanged = function () {\n var sel = this.getSelection();\n return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||\n sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset\n };\n\n ContentEditableInput.prototype.pollSelection = function () {\n if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }\n var sel = this.getSelection(), cm = this.cm;\n // On Android Chrome (version 56, at least), backspacing into an\n // uneditable block element will put the cursor in that element,\n // and then, because it's not editable, hide the virtual keyboard.\n // Because Android doesn't allow us to actually detect backspace\n // presses in a sane way, this code checks for when that happens\n // and simulates a backspace press in this case.\n if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) {\n this.cm.triggerOnKeyDown({type: \"keydown\", keyCode: 8, preventDefault: Math.abs});\n this.blur();\n this.focus();\n return\n }\n if (this.composing) { return }\n this.rememberSelection();\n var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);\n var head = domToPos(cm, sel.focusNode, sel.focusOffset);\n if (anchor && head) { runInOp(cm, function () {\n setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);\n if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; }\n }); }\n };\n\n ContentEditableInput.prototype.pollContent = function () {\n if (this.readDOMTimeout != null) {\n clearTimeout(this.readDOMTimeout);\n this.readDOMTimeout = null;\n }\n\n var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();\n var from = sel.from(), to = sel.to();\n if (from.ch == 0 && from.line > cm.firstLine())\n { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); }\n if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())\n { to = Pos(to.line + 1, 0); }\n if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }\n\n var fromIndex, fromLine, fromNode;\n if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {\n fromLine = lineNo(display.view[0].line);\n fromNode = display.view[0].node;\n } else {\n fromLine = lineNo(display.view[fromIndex].line);\n fromNode = display.view[fromIndex - 1].node.nextSibling;\n }\n var toIndex = findViewIndex(cm, to.line);\n var toLine, toNode;\n if (toIndex == display.view.length - 1) {\n toLine = display.viewTo - 1;\n toNode = display.lineDiv.lastChild;\n } else {\n toLine = lineNo(display.view[toIndex + 1].line) - 1;\n toNode = display.view[toIndex + 1].node.previousSibling;\n }\n\n if (!fromNode) { return false }\n var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));\n var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));\n while (newText.length > 1 && oldText.length > 1) {\n if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }\n else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }\n else { break }\n }\n\n var cutFront = 0, cutEnd = 0;\n var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);\n while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))\n { ++cutFront; }\n var newBot = lst(newText), oldBot = lst(oldText);\n var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),\n oldBot.length - (oldText.length == 1 ? cutFront : 0));\n while (cutEnd < maxCutEnd &&\n newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))\n { ++cutEnd; }\n // Try to move start of change to start of selection if ambiguous\n if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {\n while (cutFront && cutFront > from.ch &&\n newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {\n cutFront--;\n cutEnd++;\n }\n }\n\n newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\\u200b+/, \"\");\n newText[0] = newText[0].slice(cutFront).replace(/\\u200b+$/, \"\");\n\n var chFrom = Pos(fromLine, cutFront);\n var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);\n if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {\n replaceRange(cm.doc, newText, chFrom, chTo, \"+input\");\n return true\n }\n };\n\n ContentEditableInput.prototype.ensurePolled = function () {\n this.forceCompositionEnd();\n };\n ContentEditableInput.prototype.reset = function () {\n this.forceCompositionEnd();\n };\n ContentEditableInput.prototype.forceCompositionEnd = function () {\n if (!this.composing) { return }\n clearTimeout(this.readDOMTimeout);\n this.composing = null;\n this.updateFromDOM();\n this.div.blur();\n this.div.focus();\n };\n ContentEditableInput.prototype.readFromDOMSoon = function () {\n var this$1 = this;\n\n if (this.readDOMTimeout != null) { return }\n this.readDOMTimeout = setTimeout(function () {\n this$1.readDOMTimeout = null;\n if (this$1.composing) {\n if (this$1.composing.done) { this$1.composing = null; }\n else { return }\n }\n this$1.updateFromDOM();\n }, 80);\n };\n\n ContentEditableInput.prototype.updateFromDOM = function () {\n var this$1 = this;\n\n if (this.cm.isReadOnly() || !this.pollContent())\n { runInOp(this.cm, function () { return regChange(this$1.cm); }); }\n };\n\n ContentEditableInput.prototype.setUneditable = function (node) {\n node.contentEditable = \"false\";\n };\n\n ContentEditableInput.prototype.onKeyPress = function (e) {\n if (e.charCode == 0 || this.composing) { return }\n e.preventDefault();\n if (!this.cm.isReadOnly())\n { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }\n };\n\n ContentEditableInput.prototype.readOnlyChanged = function (val) {\n this.div.contentEditable = String(val != \"nocursor\");\n };\n\n ContentEditableInput.prototype.onContextMenu = function () {};\n ContentEditableInput.prototype.resetPosition = function () {};\n\n ContentEditableInput.prototype.needsContentAttribute = true;\n\n function posToDOM(cm, pos) {\n var view = findViewForLine(cm, pos.line);\n if (!view || view.hidden) { return null }\n var line = getLine(cm.doc, pos.line);\n var info = mapFromLineView(view, line, pos.line);\n\n var order = getOrder(line, cm.doc.direction), side = \"left\";\n if (order) {\n var partPos = getBidiPartAt(order, pos.ch);\n side = partPos % 2 ? \"right\" : \"left\";\n }\n var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);\n result.offset = result.collapse == \"right\" ? result.end : result.start;\n return result\n }\n\n function isInGutter(node) {\n for (var scan = node; scan; scan = scan.parentNode)\n { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }\n return false\n }\n\n function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }\n\n function domTextBetween(cm, from, to, fromLine, toLine) {\n var text = \"\", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false;\n function recognizeMarker(id) { return function (marker) { return marker.id == id; } }\n function close() {\n if (closing) {\n text += lineSep;\n if (extraLinebreak) { text += lineSep; }\n closing = extraLinebreak = false;\n }\n }\n function addText(str) {\n if (str) {\n close();\n text += str;\n }\n }\n function walk(node) {\n if (node.nodeType == 1) {\n var cmText = node.getAttribute(\"cm-text\");\n if (cmText) {\n addText(cmText);\n return\n }\n var markerID = node.getAttribute(\"cm-marker\"), range;\n if (markerID) {\n var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));\n if (found.length && (range = found[0].find(0)))\n { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)); }\n return\n }\n if (node.getAttribute(\"contenteditable\") == \"false\") { return }\n var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName);\n if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return }\n\n if (isBlock) { close(); }\n for (var i = 0; i < node.childNodes.length; i++)\n { walk(node.childNodes[i]); }\n\n if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; }\n if (isBlock) { closing = true; }\n } else if (node.nodeType == 3) {\n addText(node.nodeValue.replace(/\\u200b/g, \"\").replace(/\\u00a0/g, \" \"));\n }\n }\n for (;;) {\n walk(from);\n if (from == to) { break }\n from = from.nextSibling;\n extraLinebreak = false;\n }\n return text\n }\n\n function domToPos(cm, node, offset) {\n var lineNode;\n if (node == cm.display.lineDiv) {\n lineNode = cm.display.lineDiv.childNodes[offset];\n if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }\n node = null; offset = 0;\n } else {\n for (lineNode = node;; lineNode = lineNode.parentNode) {\n if (!lineNode || lineNode == cm.display.lineDiv) { return null }\n if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }\n }\n }\n for (var i = 0; i < cm.display.view.length; i++) {\n var lineView = cm.display.view[i];\n if (lineView.node == lineNode)\n { return locateNodeInLineView(lineView, node, offset) }\n }\n }\n\n function locateNodeInLineView(lineView, node, offset) {\n var wrapper = lineView.text.firstChild, bad = false;\n if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }\n if (node == wrapper) {\n bad = true;\n node = wrapper.childNodes[offset];\n offset = 0;\n if (!node) {\n var line = lineView.rest ? lst(lineView.rest) : lineView.line;\n return badPos(Pos(lineNo(line), line.text.length), bad)\n }\n }\n\n var textNode = node.nodeType == 3 ? node : null, topNode = node;\n if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {\n textNode = node.firstChild;\n if (offset) { offset = textNode.nodeValue.length; }\n }\n while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; }\n var measure = lineView.measure, maps = measure.maps;\n\n function find(textNode, topNode, offset) {\n for (var i = -1; i < (maps ? maps.length : 0); i++) {\n var map = i < 0 ? measure.map : maps[i];\n for (var j = 0; j < map.length; j += 3) {\n var curNode = map[j + 2];\n if (curNode == textNode || curNode == topNode) {\n var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);\n var ch = map[j] + offset;\n if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)]; }\n return Pos(line, ch)\n }\n }\n }\n }\n var found = find(textNode, topNode, offset);\n if (found) { return badPos(found, bad) }\n\n // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems\n for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {\n found = find(after, after.firstChild, 0);\n if (found)\n { return badPos(Pos(found.line, found.ch - dist), bad) }\n else\n { dist += after.textContent.length; }\n }\n for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {\n found = find(before, before.firstChild, -1);\n if (found)\n { return badPos(Pos(found.line, found.ch + dist$1), bad) }\n else\n { dist$1 += before.textContent.length; }\n }\n }\n\n // TEXTAREA INPUT STYLE\n\n var TextareaInput = function(cm) {\n this.cm = cm;\n // See input.poll and input.reset\n this.prevInput = \"\";\n\n // Flag that indicates whether we expect input to appear real soon\n // now (after some event like 'keypress' or 'input') and are\n // polling intensively.\n this.pollingFast = false;\n // Self-resetting timeout for the poller\n this.polling = new Delayed();\n // Used to work around IE issue with selection being forgotten when focus moves away from textarea\n this.hasSelection = false;\n this.composing = null;\n };\n\n TextareaInput.prototype.init = function (display) {\n var this$1 = this;\n\n var input = this, cm = this.cm;\n this.createField(display);\n var te = this.textarea;\n\n display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild);\n\n // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)\n if (ios) { te.style.width = \"0px\"; }\n\n on(te, \"input\", function () {\n if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; }\n input.poll();\n });\n\n on(te, \"paste\", function (e) {\n if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }\n\n cm.state.pasteIncoming = +new Date;\n input.fastPoll();\n });\n\n function prepareCopyCut(e) {\n if (signalDOMEvent(cm, e)) { return }\n if (cm.somethingSelected()) {\n setLastCopied({lineWise: false, text: cm.getSelections()});\n } else if (!cm.options.lineWiseCopyCut) {\n return\n } else {\n var ranges = copyableRanges(cm);\n setLastCopied({lineWise: true, text: ranges.text});\n if (e.type == \"cut\") {\n cm.setSelections(ranges.ranges, null, sel_dontScroll);\n } else {\n input.prevInput = \"\";\n te.value = ranges.text.join(\"\\n\");\n selectInput(te);\n }\n }\n if (e.type == \"cut\") { cm.state.cutIncoming = +new Date; }\n }\n on(te, \"cut\", prepareCopyCut);\n on(te, \"copy\", prepareCopyCut);\n\n on(display.scroller, \"paste\", function (e) {\n if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }\n if (!te.dispatchEvent) {\n cm.state.pasteIncoming = +new Date;\n input.focus();\n return\n }\n\n // Pass the `paste` event to the textarea so it's handled by its event listener.\n var event = new Event(\"paste\");\n event.clipboardData = e.clipboardData;\n te.dispatchEvent(event);\n });\n\n // Prevent normal selection in the editor (we handle our own)\n on(display.lineSpace, \"selectstart\", function (e) {\n if (!eventInWidget(display, e)) { e_preventDefault(e); }\n });\n\n on(te, \"compositionstart\", function () {\n var start = cm.getCursor(\"from\");\n if (input.composing) { input.composing.range.clear(); }\n input.composing = {\n start: start,\n range: cm.markText(start, cm.getCursor(\"to\"), {className: \"CodeMirror-composing\"})\n };\n });\n on(te, \"compositionend\", function () {\n if (input.composing) {\n input.poll();\n input.composing.range.clear();\n input.composing = null;\n }\n });\n };\n\n TextareaInput.prototype.createField = function (_display) {\n // Wraps and hides input textarea\n this.wrapper = hiddenTextarea();\n // The semihidden textarea that is focused when the editor is\n // focused, and receives input.\n this.textarea = this.wrapper.firstChild;\n };\n\n TextareaInput.prototype.prepareSelection = function () {\n // Redraw the selection and/or cursor\n var cm = this.cm, display = cm.display, doc = cm.doc;\n var result = prepareSelection(cm);\n\n // Move the hidden textarea near the cursor to prevent scrolling artifacts\n if (cm.options.moveInputWithCursor) {\n var headPos = cursorCoords(cm, doc.sel.primary().head, \"div\");\n var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();\n result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,\n headPos.top + lineOff.top - wrapOff.top));\n result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,\n headPos.left + lineOff.left - wrapOff.left));\n }\n\n return result\n };\n\n TextareaInput.prototype.showSelection = function (drawn) {\n var cm = this.cm, display = cm.display;\n removeChildrenAndAdd(display.cursorDiv, drawn.cursors);\n removeChildrenAndAdd(display.selectionDiv, drawn.selection);\n if (drawn.teTop != null) {\n this.wrapper.style.top = drawn.teTop + \"px\";\n this.wrapper.style.left = drawn.teLeft + \"px\";\n }\n };\n\n // Reset the input to correspond to the selection (or to be empty,\n // when not typing and nothing is selected)\n TextareaInput.prototype.reset = function (typing) {\n if (this.contextMenuPending || this.composing) { return }\n var cm = this.cm;\n if (cm.somethingSelected()) {\n this.prevInput = \"\";\n var content = cm.getSelection();\n this.textarea.value = content;\n if (cm.state.focused) { selectInput(this.textarea); }\n if (ie && ie_version >= 9) { this.hasSelection = content; }\n } else if (!typing) {\n this.prevInput = this.textarea.value = \"\";\n if (ie && ie_version >= 9) { this.hasSelection = null; }\n }\n };\n\n TextareaInput.prototype.getField = function () { return this.textarea };\n\n TextareaInput.prototype.supportsTouch = function () { return false };\n\n TextareaInput.prototype.focus = function () {\n if (this.cm.options.readOnly != \"nocursor\" && (!mobile || activeElt() != this.textarea)) {\n try { this.textarea.focus(); }\n catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM\n }\n };\n\n TextareaInput.prototype.blur = function () { this.textarea.blur(); };\n\n TextareaInput.prototype.resetPosition = function () {\n this.wrapper.style.top = this.wrapper.style.left = 0;\n };\n\n TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); };\n\n // Poll for input changes, using the normal rate of polling. This\n // runs as long as the editor is focused.\n TextareaInput.prototype.slowPoll = function () {\n var this$1 = this;\n\n if (this.pollingFast) { return }\n this.polling.set(this.cm.options.pollInterval, function () {\n this$1.poll();\n if (this$1.cm.state.focused) { this$1.slowPoll(); }\n });\n };\n\n // When an event has just come in that is likely to add or change\n // something in the input textarea, we poll faster, to ensure that\n // the change appears on the screen quickly.\n TextareaInput.prototype.fastPoll = function () {\n var missed = false, input = this;\n input.pollingFast = true;\n function p() {\n var changed = input.poll();\n if (!changed && !missed) {missed = true; input.polling.set(60, p);}\n else {input.pollingFast = false; input.slowPoll();}\n }\n input.polling.set(20, p);\n };\n\n // Read input from the textarea, and update the document to match.\n // When something is selected, it is present in the textarea, and\n // selected (unless it is huge, in which case a placeholder is\n // used). When nothing is selected, the cursor sits after previously\n // seen text (can be empty), which is stored in prevInput (we must\n // not reset the textarea when typing, because that breaks IME).\n TextareaInput.prototype.poll = function () {\n var this$1 = this;\n\n var cm = this.cm, input = this.textarea, prevInput = this.prevInput;\n // Since this is called a *lot*, try to bail out as cheaply as\n // possible when it is clear that nothing happened. hasSelection\n // will be the case when there is a lot of text in the textarea,\n // in which case reading its value would be expensive.\n if (this.contextMenuPending || !cm.state.focused ||\n (hasSelection(input) && !prevInput && !this.composing) ||\n cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)\n { return false }\n\n var text = input.value;\n // If nothing changed, bail.\n if (text == prevInput && !cm.somethingSelected()) { return false }\n // Work around nonsensical selection resetting in IE9/10, and\n // inexplicable appearance of private area unicode characters on\n // some key combos in Mac (#2689).\n if (ie && ie_version >= 9 && this.hasSelection === text ||\n mac && /[\\uf700-\\uf7ff]/.test(text)) {\n cm.display.input.reset();\n return false\n }\n\n if (cm.doc.sel == cm.display.selForContextMenu) {\n var first = text.charCodeAt(0);\n if (first == 0x200b && !prevInput) { prevInput = \"\\u200b\"; }\n if (first == 0x21da) { this.reset(); return this.cm.execCommand(\"undo\") }\n }\n // Find the part of the input that is actually new\n var same = 0, l = Math.min(prevInput.length, text.length);\n while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; }\n\n runInOp(cm, function () {\n applyTextInput(cm, text.slice(same), prevInput.length - same,\n null, this$1.composing ? \"*compose\" : null);\n\n // Don't leave long text in the textarea, since it makes further polling slow\n if (text.length > 1000 || text.indexOf(\"\\n\") > -1) { input.value = this$1.prevInput = \"\"; }\n else { this$1.prevInput = text; }\n\n if (this$1.composing) {\n this$1.composing.range.clear();\n this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor(\"to\"),\n {className: \"CodeMirror-composing\"});\n }\n });\n return true\n };\n\n TextareaInput.prototype.ensurePolled = function () {\n if (this.pollingFast && this.poll()) { this.pollingFast = false; }\n };\n\n TextareaInput.prototype.onKeyPress = function () {\n if (ie && ie_version >= 9) { this.hasSelection = null; }\n this.fastPoll();\n };\n\n TextareaInput.prototype.onContextMenu = function (e) {\n var input = this, cm = input.cm, display = cm.display, te = input.textarea;\n if (input.contextMenuPending) { input.contextMenuPending(); }\n var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;\n if (!pos || presto) { return } // Opera is difficult.\n\n // Reset the current text selection only if the click is done outside of the selection\n // and 'resetSelectionOnContextMenu' option is true.\n var reset = cm.options.resetSelectionOnContextMenu;\n if (reset && cm.doc.sel.contains(pos) == -1)\n { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); }\n\n var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;\n var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect();\n input.wrapper.style.cssText = \"position: static\";\n te.style.cssText = \"position: absolute; width: 30px; height: 30px;\\n top: \" + (e.clientY - wrapperBox.top - 5) + \"px; left: \" + (e.clientX - wrapperBox.left - 5) + \"px;\\n z-index: 1000; background: \" + (ie ? \"rgba(255, 255, 255, .05)\" : \"transparent\") + \";\\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);\";\n var oldScrollY;\n if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712)\n display.input.focus();\n if (webkit) { window.scrollTo(null, oldScrollY); }\n display.input.reset();\n // Adds \"Select all\" to context menu in FF\n if (!cm.somethingSelected()) { te.value = input.prevInput = \" \"; }\n input.contextMenuPending = rehide;\n display.selForContextMenu = cm.doc.sel;\n clearTimeout(display.detectingSelectAll);\n\n // Select-all will be greyed out if there's nothing to select, so\n // this adds a zero-width space so that we can later check whether\n // it got selected.\n function prepareSelectAllHack() {\n if (te.selectionStart != null) {\n var selected = cm.somethingSelected();\n var extval = \"\\u200b\" + (selected ? te.value : \"\");\n te.value = \"\\u21da\"; // Used to catch context-menu undo\n te.value = extval;\n input.prevInput = selected ? \"\" : \"\\u200b\";\n te.selectionStart = 1; te.selectionEnd = extval.length;\n // Re-set this, in case some other handler touched the\n // selection in the meantime.\n display.selForContextMenu = cm.doc.sel;\n }\n }\n function rehide() {\n if (input.contextMenuPending != rehide) { return }\n input.contextMenuPending = false;\n input.wrapper.style.cssText = oldWrapperCSS;\n te.style.cssText = oldCSS;\n if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); }\n\n // Try to detect the user choosing select-all\n if (te.selectionStart != null) {\n if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); }\n var i = 0, poll = function () {\n if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&\n te.selectionEnd > 0 && input.prevInput == \"\\u200b\") {\n operation(cm, selectAll)(cm);\n } else if (i++ < 10) {\n display.detectingSelectAll = setTimeout(poll, 500);\n } else {\n display.selForContextMenu = null;\n display.input.reset();\n }\n };\n display.detectingSelectAll = setTimeout(poll, 200);\n }\n }\n\n if (ie && ie_version >= 9) { prepareSelectAllHack(); }\n if (captureRightClick) {\n e_stop(e);\n var mouseup = function () {\n off(window, \"mouseup\", mouseup);\n setTimeout(rehide, 20);\n };\n on(window, \"mouseup\", mouseup);\n } else {\n setTimeout(rehide, 50);\n }\n };\n\n TextareaInput.prototype.readOnlyChanged = function (val) {\n if (!val) { this.reset(); }\n this.textarea.disabled = val == \"nocursor\";\n };\n\n TextareaInput.prototype.setUneditable = function () {};\n\n TextareaInput.prototype.needsContentAttribute = false;\n\n function fromTextArea(textarea, options) {\n options = options ? copyObj(options) : {};\n options.value = textarea.value;\n if (!options.tabindex && textarea.tabIndex)\n { options.tabindex = textarea.tabIndex; }\n if (!options.placeholder && textarea.placeholder)\n { options.placeholder = textarea.placeholder; }\n // Set autofocus to true if this textarea is focused, or if it has\n // autofocus and no other element is focused.\n if (options.autofocus == null) {\n var hasFocus = activeElt();\n options.autofocus = hasFocus == textarea ||\n textarea.getAttribute(\"autofocus\") != null && hasFocus == document.body;\n }\n\n function save() {textarea.value = cm.getValue();}\n\n var realSubmit;\n if (textarea.form) {\n on(textarea.form, \"submit\", save);\n // Deplorable hack to make the submit method do the right thing.\n if (!options.leaveSubmitMethodAlone) {\n var form = textarea.form;\n realSubmit = form.submit;\n try {\n var wrappedSubmit = form.submit = function () {\n save();\n form.submit = realSubmit;\n form.submit();\n form.submit = wrappedSubmit;\n };\n } catch(e) {}\n }\n }\n\n options.finishInit = function (cm) {\n cm.save = save;\n cm.getTextArea = function () { return textarea; };\n cm.toTextArea = function () {\n cm.toTextArea = isNaN; // Prevent this from being ran twice\n save();\n textarea.parentNode.removeChild(cm.getWrapperElement());\n textarea.style.display = \"\";\n if (textarea.form) {\n off(textarea.form, \"submit\", save);\n if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == \"function\")\n { textarea.form.submit = realSubmit; }\n }\n };\n };\n\n textarea.style.display = \"none\";\n var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },\n options);\n return cm\n }\n\n function addLegacyProps(CodeMirror) {\n CodeMirror.off = off;\n CodeMirror.on = on;\n CodeMirror.wheelEventPixels = wheelEventPixels;\n CodeMirror.Doc = Doc;\n CodeMirror.splitLines = splitLinesAuto;\n CodeMirror.countColumn = countColumn;\n CodeMirror.findColumn = findColumn;\n CodeMirror.isWordChar = isWordCharBasic;\n CodeMirror.Pass = Pass;\n CodeMirror.signal = signal;\n CodeMirror.Line = Line;\n CodeMirror.changeEnd = changeEnd;\n CodeMirror.scrollbarModel = scrollbarModel;\n CodeMirror.Pos = Pos;\n CodeMirror.cmpPos = cmp;\n CodeMirror.modes = modes;\n CodeMirror.mimeModes = mimeModes;\n CodeMirror.resolveMode = resolveMode;\n CodeMirror.getMode = getMode;\n CodeMirror.modeExtensions = modeExtensions;\n CodeMirror.extendMode = extendMode;\n CodeMirror.copyState = copyState;\n CodeMirror.startState = startState;\n CodeMirror.innerMode = innerMode;\n CodeMirror.commands = commands;\n CodeMirror.keyMap = keyMap;\n CodeMirror.keyName = keyName;\n CodeMirror.isModifierKey = isModifierKey;\n CodeMirror.lookupKey = lookupKey;\n CodeMirror.normalizeKeyMap = normalizeKeyMap;\n CodeMirror.StringStream = StringStream;\n CodeMirror.SharedTextMarker = SharedTextMarker;\n CodeMirror.TextMarker = TextMarker;\n CodeMirror.LineWidget = LineWidget;\n CodeMirror.e_preventDefault = e_preventDefault;\n CodeMirror.e_stopPropagation = e_stopPropagation;\n CodeMirror.e_stop = e_stop;\n CodeMirror.addClass = addClass;\n CodeMirror.contains = contains;\n CodeMirror.rmClass = rmClass;\n CodeMirror.keyNames = keyNames;\n }\n\n // EDITOR CONSTRUCTOR\n\n defineOptions(CodeMirror);\n\n addEditorMethods(CodeMirror);\n\n // Set up methods on CodeMirror's prototype to redirect to the editor's document.\n var dontDelegate = \"iter insert remove copy getEditor constructor\".split(\" \");\n for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)\n { CodeMirror.prototype[prop] = (function(method) {\n return function() {return method.apply(this.doc, arguments)}\n })(Doc.prototype[prop]); } }\n\n eventMixin(Doc);\n CodeMirror.inputStyles = {\"textarea\": TextareaInput, \"contenteditable\": ContentEditableInput};\n\n // Extra arguments are stored as the mode's dependencies, which is\n // used by (legacy) mechanisms like loadmode.js to automatically\n // load a mode. (Preferred mechanism is the require/define calls.)\n CodeMirror.defineMode = function(name/*, mode, …*/) {\n if (!CodeMirror.defaults.mode && name != \"null\") { CodeMirror.defaults.mode = name; }\n defineMode.apply(this, arguments);\n };\n\n CodeMirror.defineMIME = defineMIME;\n\n // Minimal default mode.\n CodeMirror.defineMode(\"null\", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });\n CodeMirror.defineMIME(\"text/plain\", \"null\");\n\n // EXTENSIONS\n\n CodeMirror.defineExtension = function (name, func) {\n CodeMirror.prototype[name] = func;\n };\n CodeMirror.defineDocExtension = function (name, func) {\n Doc.prototype[name] = func;\n };\n\n CodeMirror.fromTextArea = fromTextArea;\n\n addLegacyProps(CodeMirror);\n\n CodeMirror.version = \"5.51.0\";\n\n return CodeMirror;\n\n})));\n","/*global define:false */\n/**\n * Copyright 2012-2017 Craig Campbell\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * Mousetrap is a simple keyboard shortcut library for Javascript with\n * no external dependencies\n *\n * @version 1.6.5\n * @url craig.is/killing/mice\n */\n(function(window, document, undefined) {\n\n // Check if mousetrap is used inside browser, if not, return\n if (!window) {\n return;\n }\n\n /**\n * mapping of special keycodes to their corresponding keys\n *\n * everything in this dictionary cannot use keypress events\n * so it has to be here to map to the correct keycodes for\n * keyup/keydown events\n *\n * @type {Object}\n */\n var _MAP = {\n 8: 'backspace',\n 9: 'tab',\n 13: 'enter',\n 16: 'shift',\n 17: 'ctrl',\n 18: 'alt',\n 20: 'capslock',\n 27: 'esc',\n 32: 'space',\n 33: 'pageup',\n 34: 'pagedown',\n 35: 'end',\n 36: 'home',\n 37: 'left',\n 38: 'up',\n 39: 'right',\n 40: 'down',\n 45: 'ins',\n 46: 'del',\n 91: 'meta',\n 93: 'meta',\n 224: 'meta'\n };\n\n /**\n * mapping for special characters so they can support\n *\n * this dictionary is only used incase you want to bind a\n * keyup or keydown event to one of these keys\n *\n * @type {Object}\n */\n var _KEYCODE_MAP = {\n 106: '*',\n 107: '+',\n 109: '-',\n 110: '.',\n 111 : '/',\n 186: ';',\n 187: '=',\n 188: ',',\n 189: '-',\n 190: '.',\n 191: '/',\n 192: '`',\n 219: '[',\n 220: '\\\\',\n 221: ']',\n 222: '\\''\n };\n\n /**\n * this is a mapping of keys that require shift on a US keypad\n * back to the non shift equivelents\n *\n * this is so you can use keyup events with these keys\n *\n * note that this will only work reliably on US keyboards\n *\n * @type {Object}\n */\n var _SHIFT_MAP = {\n '~': '`',\n '!': '1',\n '@': '2',\n '#': '3',\n '$': '4',\n '%': '5',\n '^': '6',\n '&': '7',\n '*': '8',\n '(': '9',\n ')': '0',\n '_': '-',\n '+': '=',\n ':': ';',\n '\\\"': '\\'',\n '<': ',',\n '>': '.',\n '?': '/',\n '|': '\\\\'\n };\n\n /**\n * this is a list of special strings you can use to map\n * to modifier keys when you specify your keyboard shortcuts\n *\n * @type {Object}\n */\n var _SPECIAL_ALIASES = {\n 'option': 'alt',\n 'command': 'meta',\n 'return': 'enter',\n 'escape': 'esc',\n 'plus': '+',\n 'mod': /Mac|iPod|iPhone|iPad/.test(navigator.platform) ? 'meta' : 'ctrl'\n };\n\n /**\n * variable to store the flipped version of _MAP from above\n * needed to check if we should use keypress or not when no action\n * is specified\n *\n * @type {Object|undefined}\n */\n var _REVERSE_MAP;\n\n /**\n * loop through the f keys, f1 to f19 and add them to the map\n * programatically\n */\n for (var i = 1; i < 20; ++i) {\n _MAP[111 + i] = 'f' + i;\n }\n\n /**\n * loop through to map numbers on the numeric keypad\n */\n for (i = 0; i <= 9; ++i) {\n\n // This needs to use a string cause otherwise since 0 is falsey\n // mousetrap will never fire for numpad 0 pressed as part of a keydown\n // event.\n //\n // @see https://github.com/ccampbell/mousetrap/pull/258\n _MAP[i + 96] = i.toString();\n }\n\n /**\n * cross browser add event method\n *\n * @param {Element|HTMLDocument} object\n * @param {string} type\n * @param {Function} callback\n * @returns void\n */\n function _addEvent(object, type, callback) {\n if (object.addEventListener) {\n object.addEventListener(type, callback, false);\n return;\n }\n\n object.attachEvent('on' + type, callback);\n }\n\n /**\n * takes the event and returns the key character\n *\n * @param {Event} e\n * @return {string}\n */\n function _characterFromEvent(e) {\n\n // for keypress events we should return the character as is\n if (e.type == 'keypress') {\n var character = String.fromCharCode(e.which);\n\n // if the shift key is not pressed then it is safe to assume\n // that we want the character to be lowercase. this means if\n // you accidentally have caps lock on then your key bindings\n // will continue to work\n //\n // the only side effect that might not be desired is if you\n // bind something like 'A' cause you want to trigger an\n // event when capital A is pressed caps lock will no longer\n // trigger the event. shift+a will though.\n if (!e.shiftKey) {\n character = character.toLowerCase();\n }\n\n return character;\n }\n\n // for non keypress events the special maps are needed\n if (_MAP[e.which]) {\n return _MAP[e.which];\n }\n\n if (_KEYCODE_MAP[e.which]) {\n return _KEYCODE_MAP[e.which];\n }\n\n // if it is not in the special map\n\n // with keydown and keyup events the character seems to always\n // come in as an uppercase character whether you are pressing shift\n // or not. we should make sure it is always lowercase for comparisons\n return String.fromCharCode(e.which).toLowerCase();\n }\n\n /**\n * checks if two arrays are equal\n *\n * @param {Array} modifiers1\n * @param {Array} modifiers2\n * @returns {boolean}\n */\n function _modifiersMatch(modifiers1, modifiers2) {\n return modifiers1.sort().join(',') === modifiers2.sort().join(',');\n }\n\n /**\n * takes a key event and figures out what the modifiers are\n *\n * @param {Event} e\n * @returns {Array}\n */\n function _eventModifiers(e) {\n var modifiers = [];\n\n if (e.shiftKey) {\n modifiers.push('shift');\n }\n\n if (e.altKey) {\n modifiers.push('alt');\n }\n\n if (e.ctrlKey) {\n modifiers.push('ctrl');\n }\n\n if (e.metaKey) {\n modifiers.push('meta');\n }\n\n return modifiers;\n }\n\n /**\n * prevents default for this event\n *\n * @param {Event} e\n * @returns void\n */\n function _preventDefault(e) {\n if (e.preventDefault) {\n e.preventDefault();\n return;\n }\n\n e.returnValue = false;\n }\n\n /**\n * stops propogation for this event\n *\n * @param {Event} e\n * @returns void\n */\n function _stopPropagation(e) {\n if (e.stopPropagation) {\n e.stopPropagation();\n return;\n }\n\n e.cancelBubble = true;\n }\n\n /**\n * determines if the keycode specified is a modifier key or not\n *\n * @param {string} key\n * @returns {boolean}\n */\n function _isModifier(key) {\n return key == 'shift' || key == 'ctrl' || key == 'alt' || key == 'meta';\n }\n\n /**\n * reverses the map lookup so that we can look for specific keys\n * to see what can and can't use keypress\n *\n * @return {Object}\n */\n function _getReverseMap() {\n if (!_REVERSE_MAP) {\n _REVERSE_MAP = {};\n for (var key in _MAP) {\n\n // pull out the numeric keypad from here cause keypress should\n // be able to detect the keys from the character\n if (key > 95 && key < 112) {\n continue;\n }\n\n if (_MAP.hasOwnProperty(key)) {\n _REVERSE_MAP[_MAP[key]] = key;\n }\n }\n }\n return _REVERSE_MAP;\n }\n\n /**\n * picks the best action based on the key combination\n *\n * @param {string} key - character for key\n * @param {Array} modifiers\n * @param {string=} action passed in\n */\n function _pickBestAction(key, modifiers, action) {\n\n // if no action was picked in we should try to pick the one\n // that we think would work best for this key\n if (!action) {\n action = _getReverseMap()[key] ? 'keydown' : 'keypress';\n }\n\n // modifier keys don't work as expected with keypress,\n // switch to keydown\n if (action == 'keypress' && modifiers.length) {\n action = 'keydown';\n }\n\n return action;\n }\n\n /**\n * Converts from a string key combination to an array\n *\n * @param {string} combination like \"command+shift+l\"\n * @return {Array}\n */\n function _keysFromString(combination) {\n if (combination === '+') {\n return ['+'];\n }\n\n combination = combination.replace(/\\+{2}/g, '+plus');\n return combination.split('+');\n }\n\n /**\n * Gets info for a specific key combination\n *\n * @param {string} combination key combination (\"command+s\" or \"a\" or \"*\")\n * @param {string=} action\n * @returns {Object}\n */\n function _getKeyInfo(combination, action) {\n var keys;\n var key;\n var i;\n var modifiers = [];\n\n // take the keys from this pattern and figure out what the actual\n // pattern is all about\n keys = _keysFromString(combination);\n\n for (i = 0; i < keys.length; ++i) {\n key = keys[i];\n\n // normalize key names\n if (_SPECIAL_ALIASES[key]) {\n key = _SPECIAL_ALIASES[key];\n }\n\n // if this is not a keypress event then we should\n // be smart about using shift keys\n // this will only work for US keyboards however\n if (action && action != 'keypress' && _SHIFT_MAP[key]) {\n key = _SHIFT_MAP[key];\n modifiers.push('shift');\n }\n\n // if this key is a modifier then add it to the list of modifiers\n if (_isModifier(key)) {\n modifiers.push(key);\n }\n }\n\n // depending on what the key combination is\n // we will try to pick the best event for it\n action = _pickBestAction(key, modifiers, action);\n\n return {\n key: key,\n modifiers: modifiers,\n action: action\n };\n }\n\n function _belongsTo(element, ancestor) {\n if (element === null || element === document) {\n return false;\n }\n\n if (element === ancestor) {\n return true;\n }\n\n return _belongsTo(element.parentNode, ancestor);\n }\n\n function Mousetrap(targetElement) {\n var self = this;\n\n targetElement = targetElement || document;\n\n if (!(self instanceof Mousetrap)) {\n return new Mousetrap(targetElement);\n }\n\n /**\n * element to attach key events to\n *\n * @type {Element}\n */\n self.target = targetElement;\n\n /**\n * a list of all the callbacks setup via Mousetrap.bind()\n *\n * @type {Object}\n */\n self._callbacks = {};\n\n /**\n * direct map of string combinations to callbacks used for trigger()\n *\n * @type {Object}\n */\n self._directMap = {};\n\n /**\n * keeps track of what level each sequence is at since multiple\n * sequences can start out with the same sequence\n *\n * @type {Object}\n */\n var _sequenceLevels = {};\n\n /**\n * variable to store the setTimeout call\n *\n * @type {null|number}\n */\n var _resetTimer;\n\n /**\n * temporary state where we will ignore the next keyup\n *\n * @type {boolean|string}\n */\n var _ignoreNextKeyup = false;\n\n /**\n * temporary state where we will ignore the next keypress\n *\n * @type {boolean}\n */\n var _ignoreNextKeypress = false;\n\n /**\n * are we currently inside of a sequence?\n * type of action (\"keyup\" or \"keydown\" or \"keypress\") or false\n *\n * @type {boolean|string}\n */\n var _nextExpectedAction = false;\n\n /**\n * resets all sequence counters except for the ones passed in\n *\n * @param {Object} doNotReset\n * @returns void\n */\n function _resetSequences(doNotReset) {\n doNotReset = doNotReset || {};\n\n var activeSequences = false,\n key;\n\n for (key in _sequenceLevels) {\n if (doNotReset[key]) {\n activeSequences = true;\n continue;\n }\n _sequenceLevels[key] = 0;\n }\n\n if (!activeSequences) {\n _nextExpectedAction = false;\n }\n }\n\n /**\n * finds all callbacks that match based on the keycode, modifiers,\n * and action\n *\n * @param {string} character\n * @param {Array} modifiers\n * @param {Event|Object} e\n * @param {string=} sequenceName - name of the sequence we are looking for\n * @param {string=} combination\n * @param {number=} level\n * @returns {Array}\n */\n function _getMatches(character, modifiers, e, sequenceName, combination, level) {\n var i;\n var callback;\n var matches = [];\n var action = e.type;\n\n // if there are no events related to this keycode\n if (!self._callbacks[character]) {\n return [];\n }\n\n // if a modifier key is coming up on its own we should allow it\n if (action == 'keyup' && _isModifier(character)) {\n modifiers = [character];\n }\n\n // loop through all callbacks for the key that was pressed\n // and see if any of them match\n for (i = 0; i < self._callbacks[character].length; ++i) {\n callback = self._callbacks[character][i];\n\n // if a sequence name is not specified, but this is a sequence at\n // the wrong level then move onto the next match\n if (!sequenceName && callback.seq && _sequenceLevels[callback.seq] != callback.level) {\n continue;\n }\n\n // if the action we are looking for doesn't match the action we got\n // then we should keep going\n if (action != callback.action) {\n continue;\n }\n\n // if this is a keypress event and the meta key and control key\n // are not pressed that means that we need to only look at the\n // character, otherwise check the modifiers as well\n //\n // chrome will not fire a keypress if meta or control is down\n // safari will fire a keypress if meta or meta+shift is down\n // firefox will fire a keypress if meta or control is down\n if ((action == 'keypress' && !e.metaKey && !e.ctrlKey) || _modifiersMatch(modifiers, callback.modifiers)) {\n\n // when you bind a combination or sequence a second time it\n // should overwrite the first one. if a sequenceName or\n // combination is specified in this call it does just that\n //\n // @todo make deleting its own method?\n var deleteCombo = !sequenceName && callback.combo == combination;\n var deleteSequence = sequenceName && callback.seq == sequenceName && callback.level == level;\n if (deleteCombo || deleteSequence) {\n self._callbacks[character].splice(i, 1);\n }\n\n matches.push(callback);\n }\n }\n\n return matches;\n }\n\n /**\n * actually calls the callback function\n *\n * if your callback function returns false this will use the jquery\n * convention - prevent default and stop propogation on the event\n *\n * @param {Function} callback\n * @param {Event} e\n * @returns void\n */\n function _fireCallback(callback, e, combo, sequence) {\n\n // if this event should not happen stop here\n if (self.stopCallback(e, e.target || e.srcElement, combo, sequence)) {\n return;\n }\n\n if (callback(e, combo) === false) {\n _preventDefault(e);\n _stopPropagation(e);\n }\n }\n\n /**\n * handles a character key event\n *\n * @param {string} character\n * @param {Array} modifiers\n * @param {Event} e\n * @returns void\n */\n self._handleKey = function(character, modifiers, e) {\n var callbacks = _getMatches(character, modifiers, e);\n var i;\n var doNotReset = {};\n var maxLevel = 0;\n var processedSequenceCallback = false;\n\n // Calculate the maxLevel for sequences so we can only execute the longest callback sequence\n for (i = 0; i < callbacks.length; ++i) {\n if (callbacks[i].seq) {\n maxLevel = Math.max(maxLevel, callbacks[i].level);\n }\n }\n\n // loop through matching callbacks for this key event\n for (i = 0; i < callbacks.length; ++i) {\n\n // fire for all sequence callbacks\n // this is because if for example you have multiple sequences\n // bound such as \"g i\" and \"g t\" they both need to fire the\n // callback for matching g cause otherwise you can only ever\n // match the first one\n if (callbacks[i].seq) {\n\n // only fire callbacks for the maxLevel to prevent\n // subsequences from also firing\n //\n // for example 'a option b' should not cause 'option b' to fire\n // even though 'option b' is part of the other sequence\n //\n // any sequences that do not match here will be discarded\n // below by the _resetSequences call\n if (callbacks[i].level != maxLevel) {\n continue;\n }\n\n processedSequenceCallback = true;\n\n // keep a list of which sequences were matches for later\n doNotReset[callbacks[i].seq] = 1;\n _fireCallback(callbacks[i].callback, e, callbacks[i].combo, callbacks[i].seq);\n continue;\n }\n\n // if there were no sequence matches but we are still here\n // that means this is a regular match so we should fire that\n if (!processedSequenceCallback) {\n _fireCallback(callbacks[i].callback, e, callbacks[i].combo);\n }\n }\n\n // if the key you pressed matches the type of sequence without\n // being a modifier (ie \"keyup\" or \"keypress\") then we should\n // reset all sequences that were not matched by this event\n //\n // this is so, for example, if you have the sequence \"h a t\" and you\n // type \"h e a r t\" it does not match. in this case the \"e\" will\n // cause the sequence to reset\n //\n // modifier keys are ignored because you can have a sequence\n // that contains modifiers such as \"enter ctrl+space\" and in most\n // cases the modifier key will be pressed before the next key\n //\n // also if you have a sequence such as \"ctrl+b a\" then pressing the\n // \"b\" key will trigger a \"keypress\" and a \"keydown\"\n //\n // the \"keydown\" is expected when there is a modifier, but the\n // \"keypress\" ends up matching the _nextExpectedAction since it occurs\n // after and that causes the sequence to reset\n //\n // we ignore keypresses in a sequence that directly follow a keydown\n // for the same character\n var ignoreThisKeypress = e.type == 'keypress' && _ignoreNextKeypress;\n if (e.type == _nextExpectedAction && !_isModifier(character) && !ignoreThisKeypress) {\n _resetSequences(doNotReset);\n }\n\n _ignoreNextKeypress = processedSequenceCallback && e.type == 'keydown';\n };\n\n /**\n * handles a keydown event\n *\n * @param {Event} e\n * @returns void\n */\n function _handleKeyEvent(e) {\n\n // normalize e.which for key events\n // @see http://stackoverflow.com/questions/4285627/javascript-keycode-vs-charcode-utter-confusion\n if (typeof e.which !== 'number') {\n e.which = e.keyCode;\n }\n\n var character = _characterFromEvent(e);\n\n // no character found then stop\n if (!character) {\n return;\n }\n\n // need to use === for the character check because the character can be 0\n if (e.type == 'keyup' && _ignoreNextKeyup === character) {\n _ignoreNextKeyup = false;\n return;\n }\n\n self.handleKey(character, _eventModifiers(e), e);\n }\n\n /**\n * called to set a 1 second timeout on the specified sequence\n *\n * this is so after each key press in the sequence you have 1 second\n * to press the next key before you have to start over\n *\n * @returns void\n */\n function _resetSequenceTimer() {\n clearTimeout(_resetTimer);\n _resetTimer = setTimeout(_resetSequences, 1000);\n }\n\n /**\n * binds a key sequence to an event\n *\n * @param {string} combo - combo specified in bind call\n * @param {Array} keys\n * @param {Function} callback\n * @param {string=} action\n * @returns void\n */\n function _bindSequence(combo, keys, callback, action) {\n\n // start off by adding a sequence level record for this combination\n // and setting the level to 0\n _sequenceLevels[combo] = 0;\n\n /**\n * callback to increase the sequence level for this sequence and reset\n * all other sequences that were active\n *\n * @param {string} nextAction\n * @returns {Function}\n */\n function _increaseSequence(nextAction) {\n return function() {\n _nextExpectedAction = nextAction;\n ++_sequenceLevels[combo];\n _resetSequenceTimer();\n };\n }\n\n /**\n * wraps the specified callback inside of another function in order\n * to reset all sequence counters as soon as this sequence is done\n *\n * @param {Event} e\n * @returns void\n */\n function _callbackAndReset(e) {\n _fireCallback(callback, e, combo);\n\n // we should ignore the next key up if the action is key down\n // or keypress. this is so if you finish a sequence and\n // release the key the final key will not trigger a keyup\n if (action !== 'keyup') {\n _ignoreNextKeyup = _characterFromEvent(e);\n }\n\n // weird race condition if a sequence ends with the key\n // another sequence begins with\n setTimeout(_resetSequences, 10);\n }\n\n // loop through keys one at a time and bind the appropriate callback\n // function. for any key leading up to the final one it should\n // increase the sequence. after the final, it should reset all sequences\n //\n // if an action is specified in the original bind call then that will\n // be used throughout. otherwise we will pass the action that the\n // next key in the sequence should match. this allows a sequence\n // to mix and match keypress and keydown events depending on which\n // ones are better suited to the key provided\n for (var i = 0; i < keys.length; ++i) {\n var isFinal = i + 1 === keys.length;\n var wrappedCallback = isFinal ? _callbackAndReset : _increaseSequence(action || _getKeyInfo(keys[i + 1]).action);\n _bindSingle(keys[i], wrappedCallback, action, combo, i);\n }\n }\n\n /**\n * binds a single keyboard combination\n *\n * @param {string} combination\n * @param {Function} callback\n * @param {string=} action\n * @param {string=} sequenceName - name of sequence if part of sequence\n * @param {number=} level - what part of the sequence the command is\n * @returns void\n */\n function _bindSingle(combination, callback, action, sequenceName, level) {\n\n // store a direct mapped reference for use with Mousetrap.trigger\n self._directMap[combination + ':' + action] = callback;\n\n // make sure multiple spaces in a row become a single space\n combination = combination.replace(/\\s+/g, ' ');\n\n var sequence = combination.split(' ');\n var info;\n\n // if this pattern is a sequence of keys then run through this method\n // to reprocess each pattern one key at a time\n if (sequence.length > 1) {\n _bindSequence(combination, sequence, callback, action);\n return;\n }\n\n info = _getKeyInfo(combination, action);\n\n // make sure to initialize array if this is the first time\n // a callback is added for this key\n self._callbacks[info.key] = self._callbacks[info.key] || [];\n\n // remove an existing match if there is one\n _getMatches(info.key, info.modifiers, {type: info.action}, sequenceName, combination, level);\n\n // add this call back to the array\n // if it is a sequence put it at the beginning\n // if not put it at the end\n //\n // this is important because the way these are processed expects\n // the sequence ones to come first\n self._callbacks[info.key][sequenceName ? 'unshift' : 'push']({\n callback: callback,\n modifiers: info.modifiers,\n action: info.action,\n seq: sequenceName,\n level: level,\n combo: combination\n });\n }\n\n /**\n * binds multiple combinations to the same callback\n *\n * @param {Array} combinations\n * @param {Function} callback\n * @param {string|undefined} action\n * @returns void\n */\n self._bindMultiple = function(combinations, callback, action) {\n for (var i = 0; i < combinations.length; ++i) {\n _bindSingle(combinations[i], callback, action);\n }\n };\n\n // start!\n _addEvent(targetElement, 'keypress', _handleKeyEvent);\n _addEvent(targetElement, 'keydown', _handleKeyEvent);\n _addEvent(targetElement, 'keyup', _handleKeyEvent);\n }\n\n /**\n * binds an event to mousetrap\n *\n * can be a single key, a combination of keys separated with +,\n * an array of keys, or a sequence of keys separated by spaces\n *\n * be sure to list the modifier keys first to make sure that the\n * correct key ends up getting bound (the last key in the pattern)\n *\n * @param {string|Array} keys\n * @param {Function} callback\n * @param {string=} action - 'keypress', 'keydown', or 'keyup'\n * @returns void\n */\n Mousetrap.prototype.bind = function(keys, callback, action) {\n var self = this;\n keys = keys instanceof Array ? keys : [keys];\n self._bindMultiple.call(self, keys, callback, action);\n return self;\n };\n\n /**\n * unbinds an event to mousetrap\n *\n * the unbinding sets the callback function of the specified key combo\n * to an empty function and deletes the corresponding key in the\n * _directMap dict.\n *\n * TODO: actually remove this from the _callbacks dictionary instead\n * of binding an empty function\n *\n * the keycombo+action has to be exactly the same as\n * it was defined in the bind method\n *\n * @param {string|Array} keys\n * @param {string} action\n * @returns void\n */\n Mousetrap.prototype.unbind = function(keys, action) {\n var self = this;\n return self.bind.call(self, keys, function() {}, action);\n };\n\n /**\n * triggers an event that has already been bound\n *\n * @param {string} keys\n * @param {string=} action\n * @returns void\n */\n Mousetrap.prototype.trigger = function(keys, action) {\n var self = this;\n if (self._directMap[keys + ':' + action]) {\n self._directMap[keys + ':' + action]({}, keys);\n }\n return self;\n };\n\n /**\n * resets the library back to its initial state. this is useful\n * if you want to clear out the current keyboard shortcuts and bind\n * new ones - for example if you switch to another page\n *\n * @returns void\n */\n Mousetrap.prototype.reset = function() {\n var self = this;\n self._callbacks = {};\n self._directMap = {};\n return self;\n };\n\n /**\n * should we stop this event before firing off callbacks\n *\n * @param {Event} e\n * @param {Element} element\n * @return {boolean}\n */\n Mousetrap.prototype.stopCallback = function(e, element) {\n var self = this;\n\n // if the element has the class \"mousetrap\" then no need to stop\n if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) {\n return false;\n }\n\n if (_belongsTo(element, self.target)) {\n return false;\n }\n\n // Events originating from a shadow DOM are re-targetted and `e.target` is the shadow host,\n // not the initial event target in the shadow tree. Note that not all events cross the\n // shadow boundary.\n // For shadow trees with `mode: 'open'`, the initial event target is the first element in\n // the event’s composed path. For shadow trees with `mode: 'closed'`, the initial event\n // target cannot be obtained.\n if ('composedPath' in e && typeof e.composedPath === 'function') {\n // For open shadow trees, update `element` so that the following check works.\n var initialEventTarget = e.composedPath()[0];\n if (initialEventTarget !== e.target) {\n element = initialEventTarget;\n }\n }\n\n // stop for input, select, and textarea\n return element.tagName == 'INPUT' || element.tagName == 'SELECT' || element.tagName == 'TEXTAREA' || element.isContentEditable;\n };\n\n /**\n * exposes _handleKey publicly so it can be overwritten by extensions\n */\n Mousetrap.prototype.handleKey = function() {\n var self = this;\n return self._handleKey.apply(self, arguments);\n };\n\n /**\n * allow custom key mappings\n */\n Mousetrap.addKeycodes = function(object) {\n for (var key in object) {\n if (object.hasOwnProperty(key)) {\n _MAP[key] = object[key];\n }\n }\n _REVERSE_MAP = null;\n };\n\n /**\n * Init the global mousetrap functions\n *\n * This method is needed to allow the global mousetrap functions to work\n * now that mousetrap is a constructor function.\n */\n Mousetrap.init = function() {\n var documentMousetrap = Mousetrap(document);\n for (var method in documentMousetrap) {\n if (method.charAt(0) !== '_') {\n Mousetrap[method] = (function(method) {\n return function() {\n return documentMousetrap[method].apply(documentMousetrap, arguments);\n };\n } (method));\n }\n }\n };\n\n Mousetrap.init();\n\n // expose mousetrap to the global object\n window.Mousetrap = Mousetrap;\n\n // expose as a common js module\n if (typeof module !== 'undefined' && module.exports) {\n module.exports = Mousetrap;\n }\n\n // expose mousetrap as an AMD module\n if (typeof define === 'function' && define.amd) {\n define(function() {\n return Mousetrap;\n });\n }\n}) (typeof window !== 'undefined' ? window : null, typeof window !== 'undefined' ? document : null);\n","// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/LICENSE\n\n(function(mod) {\n if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n mod(require(\"../../lib/codemirror\"));\n else if (typeof define == \"function\" && define.amd) // AMD\n define([\"../../lib/codemirror\"], mod);\n else // Plain browser env\n mod(CodeMirror);\n})(function(CodeMirror) {\n \"use strict\";\n\n CodeMirror.defineSimpleMode = function(name, states) {\n CodeMirror.defineMode(name, function(config) {\n return CodeMirror.simpleMode(config, states);\n });\n };\n\n CodeMirror.simpleMode = function(config, states) {\n ensureState(states, \"start\");\n var states_ = {}, meta = states.meta || {}, hasIndentation = false;\n for (var state in states) if (state != meta && states.hasOwnProperty(state)) {\n var list = states_[state] = [], orig = states[state];\n for (var i = 0; i < orig.length; i++) {\n var data = orig[i];\n list.push(new Rule(data, states));\n if (data.indent || data.dedent) hasIndentation = true;\n }\n }\n var mode = {\n startState: function() {\n return {state: \"start\", pending: null,\n local: null, localState: null,\n indent: hasIndentation ? [] : null};\n },\n copyState: function(state) {\n var s = {state: state.state, pending: state.pending,\n local: state.local, localState: null,\n indent: state.indent && state.indent.slice(0)};\n if (state.localState)\n s.localState = CodeMirror.copyState(state.local.mode, state.localState);\n if (state.stack)\n s.stack = state.stack.slice(0);\n for (var pers = state.persistentStates; pers; pers = pers.next)\n s.persistentStates = {mode: pers.mode,\n spec: pers.spec,\n state: pers.state == state.localState ? s.localState : CodeMirror.copyState(pers.mode, pers.state),\n next: s.persistentStates};\n return s;\n },\n token: tokenFunction(states_, config),\n innerMode: function(state) { return state.local && {mode: state.local.mode, state: state.localState}; },\n indent: indentFunction(states_, meta)\n };\n if (meta) for (var prop in meta) if (meta.hasOwnProperty(prop))\n mode[prop] = meta[prop];\n return mode;\n };\n\n function ensureState(states, name) {\n if (!states.hasOwnProperty(name))\n throw new Error(\"Undefined state \" + name + \" in simple mode\");\n }\n\n function toRegex(val, caret) {\n if (!val) return /(?:)/;\n var flags = \"\";\n if (val instanceof RegExp) {\n if (val.ignoreCase) flags = \"i\";\n val = val.source;\n } else {\n val = String(val);\n }\n return new RegExp((caret === false ? \"\" : \"^\") + \"(?:\" + val + \")\", flags);\n }\n\n function asToken(val) {\n if (!val) return null;\n if (val.apply) return val\n if (typeof val == \"string\") return val.replace(/\\./g, \" \");\n var result = [];\n for (var i = 0; i < val.length; i++)\n result.push(val[i] && val[i].replace(/\\./g, \" \"));\n return result;\n }\n\n function Rule(data, states) {\n if (data.next || data.push) ensureState(states, data.next || data.push);\n this.regex = toRegex(data.regex);\n this.token = asToken(data.token);\n this.data = data;\n }\n\n function tokenFunction(states, config) {\n return function(stream, state) {\n if (state.pending) {\n var pend = state.pending.shift();\n if (state.pending.length == 0) state.pending = null;\n stream.pos += pend.text.length;\n return pend.token;\n }\n\n if (state.local) {\n if (state.local.end && stream.match(state.local.end)) {\n var tok = state.local.endToken || null;\n state.local = state.localState = null;\n return tok;\n } else {\n var tok = state.local.mode.token(stream, state.localState), m;\n if (state.local.endScan && (m = state.local.endScan.exec(stream.current())))\n stream.pos = stream.start + m.index;\n return tok;\n }\n }\n\n var curState = states[state.state];\n for (var i = 0; i < curState.length; i++) {\n var rule = curState[i];\n var matches = (!rule.data.sol || stream.sol()) && stream.match(rule.regex);\n if (matches) {\n if (rule.data.next) {\n state.state = rule.data.next;\n } else if (rule.data.push) {\n (state.stack || (state.stack = [])).push(state.state);\n state.state = rule.data.push;\n } else if (rule.data.pop && state.stack && state.stack.length) {\n state.state = state.stack.pop();\n }\n\n if (rule.data.mode)\n enterLocalMode(config, state, rule.data.mode, rule.token);\n if (rule.data.indent)\n state.indent.push(stream.indentation() + config.indentUnit);\n if (rule.data.dedent)\n state.indent.pop();\n var token = rule.token\n if (token && token.apply) token = token(matches)\n if (matches.length > 2 && rule.token && typeof rule.token != \"string\") {\n state.pending = [];\n for (var j = 2; j < matches.length; j++)\n if (matches[j])\n state.pending.push({text: matches[j], token: rule.token[j - 1]});\n stream.backUp(matches[0].length - (matches[1] ? matches[1].length : 0));\n return token[0];\n } else if (token && token.join) {\n return token[0];\n } else {\n return token;\n }\n }\n }\n stream.next();\n return null;\n };\n }\n\n function cmp(a, b) {\n if (a === b) return true;\n if (!a || typeof a != \"object\" || !b || typeof b != \"object\") return false;\n var props = 0;\n for (var prop in a) if (a.hasOwnProperty(prop)) {\n if (!b.hasOwnProperty(prop) || !cmp(a[prop], b[prop])) return false;\n props++;\n }\n for (var prop in b) if (b.hasOwnProperty(prop)) props--;\n return props == 0;\n }\n\n function enterLocalMode(config, state, spec, token) {\n var pers;\n if (spec.persistent) for (var p = state.persistentStates; p && !pers; p = p.next)\n if (spec.spec ? cmp(spec.spec, p.spec) : spec.mode == p.mode) pers = p;\n var mode = pers ? pers.mode : spec.mode || CodeMirror.getMode(config, spec.spec);\n var lState = pers ? pers.state : CodeMirror.startState(mode);\n if (spec.persistent && !pers)\n state.persistentStates = {mode: mode, spec: spec.spec, state: lState, next: state.persistentStates};\n\n state.localState = lState;\n state.local = {mode: mode,\n end: spec.end && toRegex(spec.end),\n endScan: spec.end && spec.forceEnd !== false && toRegex(spec.end, false),\n endToken: token && token.join ? token[token.length - 1] : token};\n }\n\n function indexOf(val, arr) {\n for (var i = 0; i < arr.length; i++) if (arr[i] === val) return true;\n }\n\n function indentFunction(states, meta) {\n return function(state, textAfter, line) {\n if (state.local && state.local.mode.indent)\n return state.local.mode.indent(state.localState, textAfter, line);\n if (state.indent == null || state.local || meta.dontIndentStates && indexOf(state.state, meta.dontIndentStates) > -1)\n return CodeMirror.Pass;\n\n var pos = state.indent.length - 1, rules = states[state.state];\n scan: for (;;) {\n for (var i = 0; i < rules.length; i++) {\n var rule = rules[i];\n if (rule.data.dedent && rule.data.dedentIfLineStart !== false) {\n var m = rule.regex.exec(textAfter);\n if (m && m[0]) {\n pos--;\n if (rule.next || rule.push) rules = states[rule.next || rule.push];\n textAfter = textAfter.slice(m[0].length);\n continue scan;\n }\n }\n }\n break;\n }\n return pos < 0 ? 0 : state.indent[pos];\n };\n }\n});\n","// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/LICENSE\n\n// Open simple dialogs on top of an editor. Relies on dialog.css.\n\n(function(mod) {\n if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n mod(require(\"../../lib/codemirror\"));\n else if (typeof define == \"function\" && define.amd) // AMD\n define([\"../../lib/codemirror\"], mod);\n else // Plain browser env\n mod(CodeMirror);\n})(function(CodeMirror) {\n function dialogDiv(cm, template, bottom) {\n var wrap = cm.getWrapperElement();\n var dialog;\n dialog = wrap.appendChild(document.createElement(\"div\"));\n if (bottom)\n dialog.className = \"CodeMirror-dialog CodeMirror-dialog-bottom\";\n else\n dialog.className = \"CodeMirror-dialog CodeMirror-dialog-top\";\n\n if (typeof template == \"string\") {\n dialog.innerHTML = template;\n } else { // Assuming it's a detached DOM element.\n dialog.appendChild(template);\n }\n CodeMirror.addClass(wrap, 'dialog-opened');\n return dialog;\n }\n\n function closeNotification(cm, newVal) {\n if (cm.state.currentNotificationClose)\n cm.state.currentNotificationClose();\n cm.state.currentNotificationClose = newVal;\n }\n\n CodeMirror.defineExtension(\"openDialog\", function(template, callback, options) {\n if (!options) options = {};\n\n closeNotification(this, null);\n\n var dialog = dialogDiv(this, template, options.bottom);\n var closed = false, me = this;\n function close(newVal) {\n if (typeof newVal == 'string') {\n inp.value = newVal;\n } else {\n if (closed) return;\n closed = true;\n CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');\n dialog.parentNode.removeChild(dialog);\n me.focus();\n\n if (options.onClose) options.onClose(dialog);\n }\n }\n\n var inp = dialog.getElementsByTagName(\"input\")[0], button;\n if (inp) {\n inp.focus();\n\n if (options.value) {\n inp.value = options.value;\n if (options.selectValueOnOpen !== false) {\n inp.select();\n }\n }\n\n if (options.onInput)\n CodeMirror.on(inp, \"input\", function(e) { options.onInput(e, inp.value, close);});\n if (options.onKeyUp)\n CodeMirror.on(inp, \"keyup\", function(e) {options.onKeyUp(e, inp.value, close);});\n\n CodeMirror.on(inp, \"keydown\", function(e) {\n if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; }\n if (e.keyCode == 27 || (options.closeOnEnter !== false && e.keyCode == 13)) {\n inp.blur();\n CodeMirror.e_stop(e);\n close();\n }\n if (e.keyCode == 13) callback(inp.value, e);\n });\n\n if (options.closeOnBlur !== false) CodeMirror.on(inp, \"blur\", close);\n } else if (button = dialog.getElementsByTagName(\"button\")[0]) {\n CodeMirror.on(button, \"click\", function() {\n close();\n me.focus();\n });\n\n if (options.closeOnBlur !== false) CodeMirror.on(button, \"blur\", close);\n\n button.focus();\n }\n return close;\n });\n\n CodeMirror.defineExtension(\"openConfirm\", function(template, callbacks, options) {\n closeNotification(this, null);\n var dialog = dialogDiv(this, template, options && options.bottom);\n var buttons = dialog.getElementsByTagName(\"button\");\n var closed = false, me = this, blurring = 1;\n function close() {\n if (closed) return;\n closed = true;\n CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');\n dialog.parentNode.removeChild(dialog);\n me.focus();\n }\n buttons[0].focus();\n for (var i = 0; i < buttons.length; ++i) {\n var b = buttons[i];\n (function(callback) {\n CodeMirror.on(b, \"click\", function(e) {\n CodeMirror.e_preventDefault(e);\n close();\n if (callback) callback(me);\n });\n })(callbacks[i]);\n CodeMirror.on(b, \"blur\", function() {\n --blurring;\n setTimeout(function() { if (blurring <= 0) close(); }, 200);\n });\n CodeMirror.on(b, \"focus\", function() { ++blurring; });\n }\n });\n\n /*\n * openNotification\n * Opens a notification, that can be closed with an optional timer\n * (default 5000ms timer) and always closes on click.\n *\n * If a notification is opened while another is opened, it will close the\n * currently opened one and open the new one immediately.\n */\n CodeMirror.defineExtension(\"openNotification\", function(template, options) {\n closeNotification(this, close);\n var dialog = dialogDiv(this, template, options && options.bottom);\n var closed = false, doneTimer;\n var duration = options && typeof options.duration !== \"undefined\" ? options.duration : 5000;\n\n function close() {\n if (closed) return;\n closed = true;\n clearTimeout(doneTimer);\n CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');\n dialog.parentNode.removeChild(dialog);\n }\n\n CodeMirror.on(dialog, 'click', function(e) {\n CodeMirror.e_preventDefault(e);\n close();\n });\n\n if (duration)\n doneTimer = setTimeout(close, duration);\n\n return close;\n });\n});\n","// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/LICENSE\n\n(function(mod) {\n if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n mod(require(\"../../lib/codemirror\"))\n else if (typeof define == \"function\" && define.amd) // AMD\n define([\"../../lib/codemirror\"], mod)\n else // Plain browser env\n mod(CodeMirror)\n})(function(CodeMirror) {\n \"use strict\"\n var Pos = CodeMirror.Pos\n\n function regexpFlags(regexp) {\n var flags = regexp.flags\n return flags != null ? flags : (regexp.ignoreCase ? \"i\" : \"\")\n + (regexp.global ? \"g\" : \"\")\n + (regexp.multiline ? \"m\" : \"\")\n }\n\n function ensureFlags(regexp, flags) {\n var current = regexpFlags(regexp), target = current\n for (var i = 0; i < flags.length; i++) if (target.indexOf(flags.charAt(i)) == -1)\n target += flags.charAt(i)\n return current == target ? regexp : new RegExp(regexp.source, target)\n }\n\n function maybeMultiline(regexp) {\n return /\\\\s|\\\\n|\\n|\\\\W|\\\\D|\\[\\^/.test(regexp.source)\n }\n\n function searchRegexpForward(doc, regexp, start) {\n regexp = ensureFlags(regexp, \"g\")\n for (var line = start.line, ch = start.ch, last = doc.lastLine(); line <= last; line++, ch = 0) {\n regexp.lastIndex = ch\n var string = doc.getLine(line), match = regexp.exec(string)\n if (match)\n return {from: Pos(line, match.index),\n to: Pos(line, match.index + match[0].length),\n match: match}\n }\n }\n\n function searchRegexpForwardMultiline(doc, regexp, start) {\n if (!maybeMultiline(regexp)) return searchRegexpForward(doc, regexp, start)\n\n regexp = ensureFlags(regexp, \"gm\")\n var string, chunk = 1\n for (var line = start.line, last = doc.lastLine(); line <= last;) {\n // This grows the search buffer in exponentially-sized chunks\n // between matches, so that nearby matches are fast and don't\n // require concatenating the whole document (in case we're\n // searching for something that has tons of matches), but at the\n // same time, the amount of retries is limited.\n for (var i = 0; i < chunk; i++) {\n if (line > last) break\n var curLine = doc.getLine(line++)\n string = string == null ? curLine : string + \"\\n\" + curLine\n }\n chunk = chunk * 2\n regexp.lastIndex = start.ch\n var match = regexp.exec(string)\n if (match) {\n var before = string.slice(0, match.index).split(\"\\n\"), inside = match[0].split(\"\\n\")\n var startLine = start.line + before.length - 1, startCh = before[before.length - 1].length\n return {from: Pos(startLine, startCh),\n to: Pos(startLine + inside.length - 1,\n inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),\n match: match}\n }\n }\n }\n\n function lastMatchIn(string, regexp, endMargin) {\n var match, from = 0\n while (from <= string.length) {\n regexp.lastIndex = from\n var newMatch = regexp.exec(string)\n if (!newMatch) break\n var end = newMatch.index + newMatch[0].length\n if (end > string.length - endMargin) break\n if (!match || end > match.index + match[0].length)\n match = newMatch\n from = newMatch.index + 1\n }\n return match\n }\n\n function searchRegexpBackward(doc, regexp, start) {\n regexp = ensureFlags(regexp, \"g\")\n for (var line = start.line, ch = start.ch, first = doc.firstLine(); line >= first; line--, ch = -1) {\n var string = doc.getLine(line)\n var match = lastMatchIn(string, regexp, ch < 0 ? 0 : string.length - ch)\n if (match)\n return {from: Pos(line, match.index),\n to: Pos(line, match.index + match[0].length),\n match: match}\n }\n }\n\n function searchRegexpBackwardMultiline(doc, regexp, start) {\n if (!maybeMultiline(regexp)) return searchRegexpBackward(doc, regexp, start)\n regexp = ensureFlags(regexp, \"gm\")\n var string, chunkSize = 1, endMargin = doc.getLine(start.line).length - start.ch\n for (var line = start.line, first = doc.firstLine(); line >= first;) {\n for (var i = 0; i < chunkSize && line >= first; i++) {\n var curLine = doc.getLine(line--)\n string = string == null ? curLine : curLine + \"\\n\" + string\n }\n chunkSize *= 2\n\n var match = lastMatchIn(string, regexp, endMargin)\n if (match) {\n var before = string.slice(0, match.index).split(\"\\n\"), inside = match[0].split(\"\\n\")\n var startLine = line + before.length, startCh = before[before.length - 1].length\n return {from: Pos(startLine, startCh),\n to: Pos(startLine + inside.length - 1,\n inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),\n match: match}\n }\n }\n }\n\n var doFold, noFold\n if (String.prototype.normalize) {\n doFold = function(str) { return str.normalize(\"NFD\").toLowerCase() }\n noFold = function(str) { return str.normalize(\"NFD\") }\n } else {\n doFold = function(str) { return str.toLowerCase() }\n noFold = function(str) { return str }\n }\n\n // Maps a position in a case-folded line back to a position in the original line\n // (compensating for codepoints increasing in number during folding)\n function adjustPos(orig, folded, pos, foldFunc) {\n if (orig.length == folded.length) return pos\n for (var min = 0, max = pos + Math.max(0, orig.length - folded.length);;) {\n if (min == max) return min\n var mid = (min + max) >> 1\n var len = foldFunc(orig.slice(0, mid)).length\n if (len == pos) return mid\n else if (len > pos) max = mid\n else min = mid + 1\n }\n }\n\n function searchStringForward(doc, query, start, caseFold) {\n // Empty string would match anything and never progress, so we\n // define it to match nothing instead.\n if (!query.length) return null\n var fold = caseFold ? doFold : noFold\n var lines = fold(query).split(/\\r|\\n\\r?/)\n\n search: for (var line = start.line, ch = start.ch, last = doc.lastLine() + 1 - lines.length; line <= last; line++, ch = 0) {\n var orig = doc.getLine(line).slice(ch), string = fold(orig)\n if (lines.length == 1) {\n var found = string.indexOf(lines[0])\n if (found == -1) continue search\n var start = adjustPos(orig, string, found, fold) + ch\n return {from: Pos(line, adjustPos(orig, string, found, fold) + ch),\n to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold) + ch)}\n } else {\n var cutFrom = string.length - lines[0].length\n if (string.slice(cutFrom) != lines[0]) continue search\n for (var i = 1; i < lines.length - 1; i++)\n if (fold(doc.getLine(line + i)) != lines[i]) continue search\n var end = doc.getLine(line + lines.length - 1), endString = fold(end), lastLine = lines[lines.length - 1]\n if (endString.slice(0, lastLine.length) != lastLine) continue search\n return {from: Pos(line, adjustPos(orig, string, cutFrom, fold) + ch),\n to: Pos(line + lines.length - 1, adjustPos(end, endString, lastLine.length, fold))}\n }\n }\n }\n\n function searchStringBackward(doc, query, start, caseFold) {\n if (!query.length) return null\n var fold = caseFold ? doFold : noFold\n var lines = fold(query).split(/\\r|\\n\\r?/)\n\n search: for (var line = start.line, ch = start.ch, first = doc.firstLine() - 1 + lines.length; line >= first; line--, ch = -1) {\n var orig = doc.getLine(line)\n if (ch > -1) orig = orig.slice(0, ch)\n var string = fold(orig)\n if (lines.length == 1) {\n var found = string.lastIndexOf(lines[0])\n if (found == -1) continue search\n return {from: Pos(line, adjustPos(orig, string, found, fold)),\n to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold))}\n } else {\n var lastLine = lines[lines.length - 1]\n if (string.slice(0, lastLine.length) != lastLine) continue search\n for (var i = 1, start = line - lines.length + 1; i < lines.length - 1; i++)\n if (fold(doc.getLine(start + i)) != lines[i]) continue search\n var top = doc.getLine(line + 1 - lines.length), topString = fold(top)\n if (topString.slice(topString.length - lines[0].length) != lines[0]) continue search\n return {from: Pos(line + 1 - lines.length, adjustPos(top, topString, top.length - lines[0].length, fold)),\n to: Pos(line, adjustPos(orig, string, lastLine.length, fold))}\n }\n }\n }\n\n function SearchCursor(doc, query, pos, options) {\n this.atOccurrence = false\n this.doc = doc\n pos = pos ? doc.clipPos(pos) : Pos(0, 0)\n this.pos = {from: pos, to: pos}\n\n var caseFold\n if (typeof options == \"object\") {\n caseFold = options.caseFold\n } else { // Backwards compat for when caseFold was the 4th argument\n caseFold = options\n options = null\n }\n\n if (typeof query == \"string\") {\n if (caseFold == null) caseFold = false\n this.matches = function(reverse, pos) {\n return (reverse ? searchStringBackward : searchStringForward)(doc, query, pos, caseFold)\n }\n } else {\n query = ensureFlags(query, \"gm\")\n if (!options || options.multiline !== false)\n this.matches = function(reverse, pos) {\n return (reverse ? searchRegexpBackwardMultiline : searchRegexpForwardMultiline)(doc, query, pos)\n }\n else\n this.matches = function(reverse, pos) {\n return (reverse ? searchRegexpBackward : searchRegexpForward)(doc, query, pos)\n }\n }\n }\n\n SearchCursor.prototype = {\n findNext: function() {return this.find(false)},\n findPrevious: function() {return this.find(true)},\n\n find: function(reverse) {\n var result = this.matches(reverse, this.doc.clipPos(reverse ? this.pos.from : this.pos.to))\n\n // Implements weird auto-growing behavior on null-matches for\n // backwards-compatiblity with the vim code (unfortunately)\n while (result && CodeMirror.cmpPos(result.from, result.to) == 0) {\n if (reverse) {\n if (result.from.ch) result.from = Pos(result.from.line, result.from.ch - 1)\n else if (result.from.line == this.doc.firstLine()) result = null\n else result = this.matches(reverse, this.doc.clipPos(Pos(result.from.line - 1)))\n } else {\n if (result.to.ch < this.doc.getLine(result.to.line).length) result.to = Pos(result.to.line, result.to.ch + 1)\n else if (result.to.line == this.doc.lastLine()) result = null\n else result = this.matches(reverse, Pos(result.to.line + 1, 0))\n }\n }\n\n if (result) {\n this.pos = result\n this.atOccurrence = true\n return this.pos.match || true\n } else {\n var end = Pos(reverse ? this.doc.firstLine() : this.doc.lastLine() + 1, 0)\n this.pos = {from: end, to: end}\n return this.atOccurrence = false\n }\n },\n\n from: function() {if (this.atOccurrence) return this.pos.from},\n to: function() {if (this.atOccurrence) return this.pos.to},\n\n replace: function(newText, origin) {\n if (!this.atOccurrence) return\n var lines = CodeMirror.splitLines(newText)\n this.doc.replaceRange(lines, this.pos.from, this.pos.to, origin)\n this.pos.to = Pos(this.pos.from.line + lines.length - 1,\n lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0))\n }\n }\n\n CodeMirror.defineExtension(\"getSearchCursor\", function(query, pos, caseFold) {\n return new SearchCursor(this.doc, query, pos, caseFold)\n })\n CodeMirror.defineDocExtension(\"getSearchCursor\", function(query, pos, caseFold) {\n return new SearchCursor(this, query, pos, caseFold)\n })\n\n CodeMirror.defineExtension(\"selectMatches\", function(query, caseFold) {\n var ranges = []\n var cur = this.getSearchCursor(query, this.getCursor(\"from\"), caseFold)\n while (cur.findNext()) {\n if (CodeMirror.cmpPos(cur.to(), this.getCursor(\"to\")) > 0) break\n ranges.push({anchor: cur.from(), head: cur.to()})\n }\n if (ranges.length)\n this.setSelections(ranges, 0)\n })\n});\n","// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/LICENSE\n\n// Define search commands. Depends on dialog.js or another\n// implementation of the openDialog method.\n\n// Replace works a little oddly -- it will do the replace on the next\n// Ctrl-G (or whatever is bound to findNext) press. You prevent a\n// replace by making sure the match is no longer selected when hitting\n// Ctrl-G.\n\n(function(mod) {\n if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n mod(require(\"../../lib/codemirror\"), require(\"./searchcursor\"), require(\"../dialog/dialog\"));\n else if (typeof define == \"function\" && define.amd) // AMD\n define([\"../../lib/codemirror\", \"./searchcursor\", \"../dialog/dialog\"], mod);\n else // Plain browser env\n mod(CodeMirror);\n})(function(CodeMirror) {\n \"use strict\";\n\n function searchOverlay(query, caseInsensitive) {\n if (typeof query == \"string\")\n query = new RegExp(query.replace(/[\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|]/g, \"\\\\$&\"), caseInsensitive ? \"gi\" : \"g\");\n else if (!query.global)\n query = new RegExp(query.source, query.ignoreCase ? \"gi\" : \"g\");\n\n return {token: function(stream) {\n query.lastIndex = stream.pos;\n var match = query.exec(stream.string);\n if (match && match.index == stream.pos) {\n stream.pos += match[0].length || 1;\n return \"searching\";\n } else if (match) {\n stream.pos = match.index;\n } else {\n stream.skipToEnd();\n }\n }};\n }\n\n function SearchState() {\n this.posFrom = this.posTo = this.lastQuery = this.query = null;\n this.overlay = null;\n }\n\n function getSearchState(cm) {\n return cm.state.search || (cm.state.search = new SearchState());\n }\n\n function queryCaseInsensitive(query) {\n return typeof query == \"string\" && query == query.toLowerCase();\n }\n\n function getSearchCursor(cm, query, pos) {\n // Heuristic: if the query string is all lowercase, do a case insensitive search.\n return cm.getSearchCursor(query, pos, {caseFold: queryCaseInsensitive(query), multiline: true});\n }\n\n function persistentDialog(cm, text, deflt, onEnter, onKeyDown) {\n cm.openDialog(text, onEnter, {\n value: deflt,\n selectValueOnOpen: true,\n closeOnEnter: false,\n onClose: function() { clearSearch(cm); },\n onKeyDown: onKeyDown\n });\n }\n\n function dialog(cm, text, shortText, deflt, f) {\n if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true});\n else f(prompt(shortText, deflt));\n }\n\n function confirmDialog(cm, text, shortText, fs) {\n if (cm.openConfirm) cm.openConfirm(text, fs);\n else if (confirm(shortText)) fs[0]();\n }\n\n function parseString(string) {\n return string.replace(/\\\\([nrt\\\\])/g, function(match, ch) {\n if (ch == \"n\") return \"\\n\"\n if (ch == \"r\") return \"\\r\"\n if (ch == \"t\") return \"\\t\"\n if (ch == \"\\\\\") return \"\\\\\"\n return match\n })\n }\n\n function parseQuery(query) {\n var isRE = query.match(/^\\/(.*)\\/([a-z]*)$/);\n if (isRE) {\n try { query = new RegExp(isRE[1], isRE[2].indexOf(\"i\") == -1 ? \"\" : \"i\"); }\n catch(e) {} // Not a regular expression after all, do a string search\n } else {\n query = parseString(query)\n }\n if (typeof query == \"string\" ? query == \"\" : query.test(\"\"))\n query = /x^/;\n return query;\n }\n\n function startSearch(cm, state, query) {\n state.queryText = query;\n state.query = parseQuery(query);\n cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));\n state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));\n cm.addOverlay(state.overlay);\n if (cm.showMatchesOnScrollbar) {\n if (state.annotate) { state.annotate.clear(); state.annotate = null; }\n state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query));\n }\n }\n\n function doSearch(cm, rev, persistent, immediate) {\n var state = getSearchState(cm);\n if (state.query) return findNext(cm, rev);\n var q = cm.getSelection() || state.lastQuery;\n if (q instanceof RegExp && q.source == \"x^\") q = null\n if (persistent && cm.openDialog) {\n var hiding = null\n var searchNext = function(query, event) {\n CodeMirror.e_stop(event);\n if (!query) return;\n if (query != state.queryText) {\n startSearch(cm, state, query);\n state.posFrom = state.posTo = cm.getCursor();\n }\n if (hiding) hiding.style.opacity = 1\n findNext(cm, event.shiftKey, function(_, to) {\n var dialog\n if (to.line < 3 && document.querySelector &&\n (dialog = cm.display.wrapper.querySelector(\".CodeMirror-dialog\")) &&\n dialog.getBoundingClientRect().bottom - 4 > cm.cursorCoords(to, \"window\").top)\n (hiding = dialog).style.opacity = .4\n })\n };\n persistentDialog(cm, getQueryDialog(cm), q, searchNext, function(event, query) {\n var keyName = CodeMirror.keyName(event)\n var extra = cm.getOption('extraKeys'), cmd = (extra && extra[keyName]) || CodeMirror.keyMap[cm.getOption(\"keyMap\")][keyName]\n if (cmd == \"findNext\" || cmd == \"findPrev\" ||\n cmd == \"findPersistentNext\" || cmd == \"findPersistentPrev\") {\n CodeMirror.e_stop(event);\n startSearch(cm, getSearchState(cm), query);\n cm.execCommand(cmd);\n } else if (cmd == \"find\" || cmd == \"findPersistent\") {\n CodeMirror.e_stop(event);\n searchNext(query, event);\n }\n });\n if (immediate && q) {\n startSearch(cm, state, q);\n findNext(cm, rev);\n }\n } else {\n dialog(cm, getQueryDialog(cm), \"Search for:\", q, function(query) {\n if (query && !state.query) cm.operation(function() {\n startSearch(cm, state, query);\n state.posFrom = state.posTo = cm.getCursor();\n findNext(cm, rev);\n });\n });\n }\n }\n\n function findNext(cm, rev, callback) {cm.operation(function() {\n var state = getSearchState(cm);\n var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);\n if (!cursor.find(rev)) {\n cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));\n if (!cursor.find(rev)) return;\n }\n cm.setSelection(cursor.from(), cursor.to());\n cm.scrollIntoView({from: cursor.from(), to: cursor.to()}, 20);\n state.posFrom = cursor.from(); state.posTo = cursor.to();\n if (callback) callback(cursor.from(), cursor.to())\n });}\n\n function clearSearch(cm) {cm.operation(function() {\n var state = getSearchState(cm);\n state.lastQuery = state.query;\n if (!state.query) return;\n state.query = state.queryText = null;\n cm.removeOverlay(state.overlay);\n if (state.annotate) { state.annotate.clear(); state.annotate = null; }\n });}\n\n\n function getQueryDialog(cm) {\n return '' + cm.phrase(\"Search:\") + ' ' + cm.phrase(\"(Use /re/ syntax for regexp search)\") + '';\n }\n function getReplaceQueryDialog(cm) {\n return ' ' + cm.phrase(\"(Use /re/ syntax for regexp search)\") + '';\n }\n function getReplacementQueryDialog(cm) {\n return '' + cm.phrase(\"With:\") + ' ';\n }\n function getDoReplaceConfirm(cm) {\n return '' + cm.phrase(\"Replace?\") + ' ';\n }\n\n function replaceAll(cm, query, text) {\n cm.operation(function() {\n for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {\n if (typeof query != \"string\") {\n var match = cm.getRange(cursor.from(), cursor.to()).match(query);\n cursor.replace(text.replace(/\\$(\\d)/g, function(_, i) {return match[i];}));\n } else cursor.replace(text);\n }\n });\n }\n\n function replace(cm, all) {\n if (cm.getOption(\"readOnly\")) return;\n var query = cm.getSelection() || getSearchState(cm).lastQuery;\n var dialogText = '' + (all ? cm.phrase(\"Replace all:\") : cm.phrase(\"Replace:\")) + '';\n dialog(cm, dialogText + getReplaceQueryDialog(cm), dialogText, query, function(query) {\n if (!query) return;\n query = parseQuery(query);\n dialog(cm, getReplacementQueryDialog(cm), cm.phrase(\"Replace with:\"), \"\", function(text) {\n text = parseString(text)\n if (all) {\n replaceAll(cm, query, text)\n } else {\n clearSearch(cm);\n var cursor = getSearchCursor(cm, query, cm.getCursor(\"from\"));\n var advance = function() {\n var start = cursor.from(), match;\n if (!(match = cursor.findNext())) {\n cursor = getSearchCursor(cm, query);\n if (!(match = cursor.findNext()) ||\n (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;\n }\n cm.setSelection(cursor.from(), cursor.to());\n cm.scrollIntoView({from: cursor.from(), to: cursor.to()});\n confirmDialog(cm, getDoReplaceConfirm(cm), cm.phrase(\"Replace?\"),\n [function() {doReplace(match);}, advance,\n function() {replaceAll(cm, query, text)}]);\n };\n var doReplace = function(match) {\n cursor.replace(typeof query == \"string\" ? text :\n text.replace(/\\$(\\d)/g, function(_, i) {return match[i];}));\n advance();\n };\n advance();\n }\n });\n });\n }\n\n CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};\n CodeMirror.commands.findPersistent = function(cm) {clearSearch(cm); doSearch(cm, false, true);};\n CodeMirror.commands.findPersistentNext = function(cm) {doSearch(cm, false, true, true);};\n CodeMirror.commands.findPersistentPrev = function(cm) {doSearch(cm, true, true, true);};\n CodeMirror.commands.findNext = doSearch;\n CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};\n CodeMirror.commands.clearSearch = clearSearch;\n CodeMirror.commands.replace = replace;\n CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};\n});\n","// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/LICENSE\n\n(function(mod) {\n if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n mod(require(\"../../lib/codemirror\"));\n else if (typeof define == \"function\" && define.amd) // AMD\n define([\"../../lib/codemirror\"], mod);\n else // Plain browser env\n mod(CodeMirror);\n})(function(CodeMirror) {\n \"use strict\";\n\n CodeMirror.defineOption(\"rulers\", false, function(cm, val) {\n if (cm.state.rulerDiv) {\n cm.state.rulerDiv.parentElement.removeChild(cm.state.rulerDiv)\n cm.state.rulerDiv = null\n cm.off(\"refresh\", drawRulers)\n }\n if (val && val.length) {\n cm.state.rulerDiv = cm.display.lineSpace.parentElement.insertBefore(document.createElement(\"div\"), cm.display.lineSpace)\n cm.state.rulerDiv.className = \"CodeMirror-rulers\"\n drawRulers(cm)\n cm.on(\"refresh\", drawRulers)\n }\n });\n\n function drawRulers(cm) {\n cm.state.rulerDiv.textContent = \"\"\n var val = cm.getOption(\"rulers\");\n var cw = cm.defaultCharWidth();\n var left = cm.charCoords(CodeMirror.Pos(cm.firstLine(), 0), \"div\").left;\n cm.state.rulerDiv.style.minHeight = (cm.display.scroller.offsetHeight + 30) + \"px\";\n for (var i = 0; i < val.length; i++) {\n var elt = document.createElement(\"div\");\n elt.className = \"CodeMirror-ruler\";\n var col, conf = val[i];\n if (typeof conf == \"number\") {\n col = conf;\n } else {\n col = conf.column;\n if (conf.className) elt.className += \" \" + conf.className;\n if (conf.color) elt.style.borderColor = conf.color;\n if (conf.lineStyle) elt.style.borderLeftStyle = conf.lineStyle;\n if (conf.width) elt.style.borderLeftWidth = conf.width;\n }\n elt.style.left = (left + col * cw) + \"px\";\n cm.state.rulerDiv.appendChild(elt)\n }\n }\n});\n","// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/LICENSE\n\n(function(mod) {\n if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n mod(require(\"../../lib/codemirror\"));\n else if (typeof define == \"function\" && define.amd) // AMD\n define([\"../../lib/codemirror\"], mod);\n else // Plain browser env\n mod(CodeMirror);\n})(function(CodeMirror) {\n CodeMirror.defineOption(\"showTrailingSpace\", false, function(cm, val, prev) {\n if (prev == CodeMirror.Init) prev = false;\n if (prev && !val)\n cm.removeOverlay(\"trailingspace\");\n else if (!prev && val)\n cm.addOverlay({\n token: function(stream) {\n for (var l = stream.string.length, i = l; i && /\\s/.test(stream.string.charAt(i - 1)); --i) {}\n if (i > stream.pos) { stream.pos = i; return null; }\n stream.pos = l;\n return \"trailingspace\";\n },\n name: \"trailingspace\"\n });\n });\n});\n","// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/LICENSE\n\n(function(mod) {\n if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n mod(require(\"../../lib/codemirror\"));\n else if (typeof define == \"function\" && define.amd) // AMD\n define([\"../../lib/codemirror\"], mod);\n else // Plain browser env\n mod(CodeMirror);\n})(function(CodeMirror) {\n \"use strict\";\n\n function doFold(cm, pos, options, force) {\n if (options && options.call) {\n var finder = options;\n options = null;\n } else {\n var finder = getOption(cm, options, \"rangeFinder\");\n }\n if (typeof pos == \"number\") pos = CodeMirror.Pos(pos, 0);\n var minSize = getOption(cm, options, \"minFoldSize\");\n\n function getRange(allowFolded) {\n var range = finder(cm, pos);\n if (!range || range.to.line - range.from.line < minSize) return null;\n var marks = cm.findMarksAt(range.from);\n for (var i = 0; i < marks.length; ++i) {\n if (marks[i].__isFold && force !== \"fold\") {\n if (!allowFolded) return null;\n range.cleared = true;\n marks[i].clear();\n }\n }\n return range;\n }\n\n var range = getRange(true);\n if (getOption(cm, options, \"scanUp\")) while (!range && pos.line > cm.firstLine()) {\n pos = CodeMirror.Pos(pos.line - 1, 0);\n range = getRange(false);\n }\n if (!range || range.cleared || force === \"unfold\") return;\n\n var myWidget = makeWidget(cm, options, range);\n CodeMirror.on(myWidget, \"mousedown\", function(e) {\n myRange.clear();\n CodeMirror.e_preventDefault(e);\n });\n var myRange = cm.markText(range.from, range.to, {\n replacedWith: myWidget,\n clearOnEnter: getOption(cm, options, \"clearOnEnter\"),\n __isFold: true\n });\n myRange.on(\"clear\", function(from, to) {\n CodeMirror.signal(cm, \"unfold\", cm, from, to);\n });\n CodeMirror.signal(cm, \"fold\", cm, range.from, range.to);\n }\n\n function makeWidget(cm, options, range) {\n var widget = getOption(cm, options, \"widget\");\n\n if (typeof widget == \"function\") {\n widget = widget(range.from, range.to);\n }\n\n if (typeof widget == \"string\") {\n var text = document.createTextNode(widget);\n widget = document.createElement(\"span\");\n widget.appendChild(text);\n widget.className = \"CodeMirror-foldmarker\";\n } else if (widget) {\n widget = widget.cloneNode(true)\n }\n return widget;\n }\n\n // Clumsy backwards-compatible interface\n CodeMirror.newFoldFunction = function(rangeFinder, widget) {\n return function(cm, pos) { doFold(cm, pos, {rangeFinder: rangeFinder, widget: widget}); };\n };\n\n // New-style interface\n CodeMirror.defineExtension(\"foldCode\", function(pos, options, force) {\n doFold(this, pos, options, force);\n });\n\n CodeMirror.defineExtension(\"isFolded\", function(pos) {\n var marks = this.findMarksAt(pos);\n for (var i = 0; i < marks.length; ++i)\n if (marks[i].__isFold) return true;\n });\n\n CodeMirror.commands.toggleFold = function(cm) {\n cm.foldCode(cm.getCursor());\n };\n CodeMirror.commands.fold = function(cm) {\n cm.foldCode(cm.getCursor(), null, \"fold\");\n };\n CodeMirror.commands.unfold = function(cm) {\n cm.foldCode(cm.getCursor(), null, \"unfold\");\n };\n CodeMirror.commands.foldAll = function(cm) {\n cm.operation(function() {\n for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++)\n cm.foldCode(CodeMirror.Pos(i, 0), null, \"fold\");\n });\n };\n CodeMirror.commands.unfoldAll = function(cm) {\n cm.operation(function() {\n for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++)\n cm.foldCode(CodeMirror.Pos(i, 0), null, \"unfold\");\n });\n };\n\n CodeMirror.registerHelper(\"fold\", \"combine\", function() {\n var funcs = Array.prototype.slice.call(arguments, 0);\n return function(cm, start) {\n for (var i = 0; i < funcs.length; ++i) {\n var found = funcs[i](cm, start);\n if (found) return found;\n }\n };\n });\n\n CodeMirror.registerHelper(\"fold\", \"auto\", function(cm, start) {\n var helpers = cm.getHelpers(start, \"fold\");\n for (var i = 0; i < helpers.length; i++) {\n var cur = helpers[i](cm, start);\n if (cur) return cur;\n }\n });\n\n var defaultOptions = {\n rangeFinder: CodeMirror.fold.auto,\n widget: \"\\u2194\",\n minFoldSize: 0,\n scanUp: false,\n clearOnEnter: true\n };\n\n CodeMirror.defineOption(\"foldOptions\", null);\n\n function getOption(cm, options, name) {\n if (options && options[name] !== undefined)\n return options[name];\n var editorOptions = cm.options.foldOptions;\n if (editorOptions && editorOptions[name] !== undefined)\n return editorOptions[name];\n return defaultOptions[name];\n }\n\n CodeMirror.defineExtension(\"foldOption\", function(options, name) {\n return getOption(this, options, name);\n });\n});\n","// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/LICENSE\n\n(function(mod) {\n if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n mod(require(\"../../lib/codemirror\"), require(\"./foldcode\"));\n else if (typeof define == \"function\" && define.amd) // AMD\n define([\"../../lib/codemirror\", \"./foldcode\"], mod);\n else // Plain browser env\n mod(CodeMirror);\n})(function(CodeMirror) {\n \"use strict\";\n\n CodeMirror.defineOption(\"foldGutter\", false, function(cm, val, old) {\n if (old && old != CodeMirror.Init) {\n cm.clearGutter(cm.state.foldGutter.options.gutter);\n cm.state.foldGutter = null;\n cm.off(\"gutterClick\", onGutterClick);\n cm.off(\"changes\", onChange);\n cm.off(\"viewportChange\", onViewportChange);\n cm.off(\"fold\", onFold);\n cm.off(\"unfold\", onFold);\n cm.off(\"swapDoc\", onChange);\n }\n if (val) {\n cm.state.foldGutter = new State(parseOptions(val));\n updateInViewport(cm);\n cm.on(\"gutterClick\", onGutterClick);\n cm.on(\"changes\", onChange);\n cm.on(\"viewportChange\", onViewportChange);\n cm.on(\"fold\", onFold);\n cm.on(\"unfold\", onFold);\n cm.on(\"swapDoc\", onChange);\n }\n });\n\n var Pos = CodeMirror.Pos;\n\n function State(options) {\n this.options = options;\n this.from = this.to = 0;\n }\n\n function parseOptions(opts) {\n if (opts === true) opts = {};\n if (opts.gutter == null) opts.gutter = \"CodeMirror-foldgutter\";\n if (opts.indicatorOpen == null) opts.indicatorOpen = \"CodeMirror-foldgutter-open\";\n if (opts.indicatorFolded == null) opts.indicatorFolded = \"CodeMirror-foldgutter-folded\";\n return opts;\n }\n\n function isFolded(cm, line) {\n var marks = cm.findMarks(Pos(line, 0), Pos(line + 1, 0));\n for (var i = 0; i < marks.length; ++i) {\n if (marks[i].__isFold) {\n var fromPos = marks[i].find(-1);\n if (fromPos && fromPos.line === line)\n return marks[i];\n }\n }\n }\n\n function marker(spec) {\n if (typeof spec == \"string\") {\n var elt = document.createElement(\"div\");\n elt.className = spec + \" CodeMirror-guttermarker-subtle\";\n return elt;\n } else {\n return spec.cloneNode(true);\n }\n }\n\n function updateFoldInfo(cm, from, to) {\n var opts = cm.state.foldGutter.options, cur = from - 1;\n var minSize = cm.foldOption(opts, \"minFoldSize\");\n var func = cm.foldOption(opts, \"rangeFinder\");\n // we can reuse the built-in indicator element if its className matches the new state\n var clsFolded = typeof opts.indicatorFolded == \"string\" && classTest(opts.indicatorFolded);\n var clsOpen = typeof opts.indicatorOpen == \"string\" && classTest(opts.indicatorOpen);\n cm.eachLine(from, to, function(line) {\n ++cur;\n var mark = null;\n var old = line.gutterMarkers;\n if (old) old = old[opts.gutter];\n if (isFolded(cm, cur)) {\n if (clsFolded && old && clsFolded.test(old.className)) return;\n mark = marker(opts.indicatorFolded);\n } else {\n var pos = Pos(cur, 0);\n var range = func && func(cm, pos);\n if (range && range.to.line - range.from.line >= minSize) {\n if (clsOpen && old && clsOpen.test(old.className)) return;\n mark = marker(opts.indicatorOpen);\n }\n }\n if (!mark && !old) return;\n cm.setGutterMarker(line, opts.gutter, mark);\n });\n }\n\n // copied from CodeMirror/src/util/dom.js\n function classTest(cls) { return new RegExp(\"(^|\\\\s)\" + cls + \"(?:$|\\\\s)\\\\s*\") }\n\n function updateInViewport(cm) {\n var vp = cm.getViewport(), state = cm.state.foldGutter;\n if (!state) return;\n cm.operation(function() {\n updateFoldInfo(cm, vp.from, vp.to);\n });\n state.from = vp.from; state.to = vp.to;\n }\n\n function onGutterClick(cm, line, gutter) {\n var state = cm.state.foldGutter;\n if (!state) return;\n var opts = state.options;\n if (gutter != opts.gutter) return;\n var folded = isFolded(cm, line);\n if (folded) folded.clear();\n else cm.foldCode(Pos(line, 0), opts);\n }\n\n function onChange(cm) {\n var state = cm.state.foldGutter;\n if (!state) return;\n var opts = state.options;\n state.from = state.to = 0;\n clearTimeout(state.changeUpdate);\n state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, opts.foldOnChangeTimeSpan || 600);\n }\n\n function onViewportChange(cm) {\n var state = cm.state.foldGutter;\n if (!state) return;\n var opts = state.options;\n clearTimeout(state.changeUpdate);\n state.changeUpdate = setTimeout(function() {\n var vp = cm.getViewport();\n if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {\n updateInViewport(cm);\n } else {\n cm.operation(function() {\n if (vp.from < state.from) {\n updateFoldInfo(cm, vp.from, state.from);\n state.from = vp.from;\n }\n if (vp.to > state.to) {\n updateFoldInfo(cm, state.to, vp.to);\n state.to = vp.to;\n }\n });\n }\n }, opts.updateViewportTimeSpan || 400);\n }\n\n function onFold(cm, from) {\n var state = cm.state.foldGutter;\n if (!state) return;\n var line = from.line;\n if (line >= state.from && line < state.to)\n updateFoldInfo(cm, line, line + 1);\n }\n});\n","// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/LICENSE\n\n(function(mod) {\n if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n mod(require(\"../../lib/codemirror\"));\n else if (typeof define == \"function\" && define.amd) // AMD\n define([\"../../lib/codemirror\"], mod);\n else // Plain browser env\n mod(CodeMirror);\n})(function(CodeMirror) {\n \"use strict\";\n\n var HINT_ELEMENT_CLASS = \"CodeMirror-hint\";\n var ACTIVE_HINT_ELEMENT_CLASS = \"CodeMirror-hint-active\";\n\n // This is the old interface, kept around for now to stay\n // backwards-compatible.\n CodeMirror.showHint = function(cm, getHints, options) {\n if (!getHints) return cm.showHint(options);\n if (options && options.async) getHints.async = true;\n var newOpts = {hint: getHints};\n if (options) for (var prop in options) newOpts[prop] = options[prop];\n return cm.showHint(newOpts);\n };\n\n CodeMirror.defineExtension(\"showHint\", function(options) {\n options = parseOptions(this, this.getCursor(\"start\"), options);\n var selections = this.listSelections()\n if (selections.length > 1) return;\n // By default, don't allow completion when something is selected.\n // A hint function can have a `supportsSelection` property to\n // indicate that it can handle selections.\n if (this.somethingSelected()) {\n if (!options.hint.supportsSelection) return;\n // Don't try with cross-line selections\n for (var i = 0; i < selections.length; i++)\n if (selections[i].head.line != selections[i].anchor.line) return;\n }\n\n if (this.state.completionActive) this.state.completionActive.close();\n var completion = this.state.completionActive = new Completion(this, options);\n if (!completion.options.hint) return;\n\n CodeMirror.signal(this, \"startCompletion\", this);\n completion.update(true);\n });\n\n CodeMirror.defineExtension(\"closeHint\", function() {\n if (this.state.completionActive) this.state.completionActive.close()\n })\n\n function Completion(cm, options) {\n this.cm = cm;\n this.options = options;\n this.widget = null;\n this.debounce = 0;\n this.tick = 0;\n this.startPos = this.cm.getCursor(\"start\");\n this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length;\n\n var self = this;\n cm.on(\"cursorActivity\", this.activityFunc = function() { self.cursorActivity(); });\n }\n\n var requestAnimationFrame = window.requestAnimationFrame || function(fn) {\n return setTimeout(fn, 1000/60);\n };\n var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;\n\n Completion.prototype = {\n close: function() {\n if (!this.active()) return;\n this.cm.state.completionActive = null;\n this.tick = null;\n this.cm.off(\"cursorActivity\", this.activityFunc);\n\n if (this.widget && this.data) CodeMirror.signal(this.data, \"close\");\n if (this.widget) this.widget.close();\n CodeMirror.signal(this.cm, \"endCompletion\", this.cm);\n },\n\n active: function() {\n return this.cm.state.completionActive == this;\n },\n\n pick: function(data, i) {\n var completion = data.list[i];\n if (completion.hint) completion.hint(this.cm, data, completion);\n else this.cm.replaceRange(getText(completion), completion.from || data.from,\n completion.to || data.to, \"complete\");\n CodeMirror.signal(data, \"pick\", completion);\n this.close();\n },\n\n cursorActivity: function() {\n if (this.debounce) {\n cancelAnimationFrame(this.debounce);\n this.debounce = 0;\n }\n\n var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line);\n if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch ||\n pos.ch < this.startPos.ch || this.cm.somethingSelected() ||\n (!pos.ch || this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) {\n this.close();\n } else {\n var self = this;\n this.debounce = requestAnimationFrame(function() {self.update();});\n if (this.widget) this.widget.disable();\n }\n },\n\n update: function(first) {\n if (this.tick == null) return\n var self = this, myTick = ++this.tick\n fetchHints(this.options.hint, this.cm, this.options, function(data) {\n if (self.tick == myTick) self.finishUpdate(data, first)\n })\n },\n\n finishUpdate: function(data, first) {\n if (this.data) CodeMirror.signal(this.data, \"update\");\n\n var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle);\n if (this.widget) this.widget.close();\n\n this.data = data;\n\n if (data && data.list.length) {\n if (picked && data.list.length == 1) {\n this.pick(data, 0);\n } else {\n this.widget = new Widget(this, data);\n CodeMirror.signal(data, \"shown\");\n }\n }\n }\n };\n\n function parseOptions(cm, pos, options) {\n var editor = cm.options.hintOptions;\n var out = {};\n for (var prop in defaultOptions) out[prop] = defaultOptions[prop];\n if (editor) for (var prop in editor)\n if (editor[prop] !== undefined) out[prop] = editor[prop];\n if (options) for (var prop in options)\n if (options[prop] !== undefined) out[prop] = options[prop];\n if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos)\n return out;\n }\n\n function getText(completion) {\n if (typeof completion == \"string\") return completion;\n else return completion.text;\n }\n\n function buildKeyMap(completion, handle) {\n var baseMap = {\n Up: function() {handle.moveFocus(-1);},\n Down: function() {handle.moveFocus(1);},\n PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);},\n PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);},\n Home: function() {handle.setFocus(0);},\n End: function() {handle.setFocus(handle.length - 1);},\n Enter: handle.pick,\n Tab: handle.pick,\n Esc: handle.close\n };\n\n var mac = /Mac/.test(navigator.platform);\n\n if (mac) {\n baseMap[\"Ctrl-P\"] = function() {handle.moveFocus(-1);};\n baseMap[\"Ctrl-N\"] = function() {handle.moveFocus(1);};\n }\n\n var custom = completion.options.customKeys;\n var ourMap = custom ? {} : baseMap;\n function addBinding(key, val) {\n var bound;\n if (typeof val != \"string\")\n bound = function(cm) { return val(cm, handle); };\n // This mechanism is deprecated\n else if (baseMap.hasOwnProperty(val))\n bound = baseMap[val];\n else\n bound = val;\n ourMap[key] = bound;\n }\n if (custom)\n for (var key in custom) if (custom.hasOwnProperty(key))\n addBinding(key, custom[key]);\n var extra = completion.options.extraKeys;\n if (extra)\n for (var key in extra) if (extra.hasOwnProperty(key))\n addBinding(key, extra[key]);\n return ourMap;\n }\n\n function getHintElement(hintsElement, el) {\n while (el && el != hintsElement) {\n if (el.nodeName.toUpperCase() === \"LI\" && el.parentNode == hintsElement) return el;\n el = el.parentNode;\n }\n }\n\n function Widget(completion, data) {\n this.completion = completion;\n this.data = data;\n this.picked = false;\n var widget = this, cm = completion.cm;\n var ownerDocument = cm.getInputField().ownerDocument;\n var parentWindow = ownerDocument.defaultView || ownerDocument.parentWindow;\n\n var hints = this.hints = ownerDocument.createElement(\"ul\");\n var theme = completion.cm.options.theme;\n hints.className = \"CodeMirror-hints \" + theme;\n this.selectedHint = data.selectedHint || 0;\n\n var completions = data.list;\n for (var i = 0; i < completions.length; ++i) {\n var elt = hints.appendChild(ownerDocument.createElement(\"li\")), cur = completions[i];\n var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? \"\" : \" \" + ACTIVE_HINT_ELEMENT_CLASS);\n if (cur.className != null) className = cur.className + \" \" + className;\n elt.className = className;\n if (cur.render) cur.render(elt, data, cur);\n else elt.appendChild(ownerDocument.createTextNode(cur.displayText || getText(cur)));\n elt.hintId = i;\n }\n\n var container = completion.options.container || ownerDocument.body;\n var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null);\n var left = pos.left, top = pos.bottom, below = true;\n var offsetLeft = 0, offsetTop = 0;\n if (container !== ownerDocument.body) {\n // We offset the cursor position because left and top are relative to the offsetParent's top left corner.\n var isContainerPositioned = ['absolute', 'relative', 'fixed'].indexOf(parentWindow.getComputedStyle(container).position) !== -1;\n var offsetParent = isContainerPositioned ? container : container.offsetParent;\n var offsetParentPosition = offsetParent.getBoundingClientRect();\n var bodyPosition = ownerDocument.body.getBoundingClientRect();\n offsetLeft = (offsetParentPosition.left - bodyPosition.left - offsetParent.scrollLeft);\n offsetTop = (offsetParentPosition.top - bodyPosition.top - offsetParent.scrollTop);\n }\n hints.style.left = (left - offsetLeft) + \"px\";\n hints.style.top = (top - offsetTop) + \"px\";\n\n // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.\n var winW = parentWindow.innerWidth || Math.max(ownerDocument.body.offsetWidth, ownerDocument.documentElement.offsetWidth);\n var winH = parentWindow.innerHeight || Math.max(ownerDocument.body.offsetHeight, ownerDocument.documentElement.offsetHeight);\n container.appendChild(hints);\n var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;\n var scrolls = hints.scrollHeight > hints.clientHeight + 1\n var startScroll = cm.getScrollInfo();\n\n if (overlapY > 0) {\n var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);\n if (curTop - height > 0) { // Fits above cursor\n hints.style.top = (top = pos.top - height - offsetTop) + \"px\";\n below = false;\n } else if (height > winH) {\n hints.style.height = (winH - 5) + \"px\";\n hints.style.top = (top = pos.bottom - box.top - offsetTop) + \"px\";\n var cursor = cm.getCursor();\n if (data.from.ch != cursor.ch) {\n pos = cm.cursorCoords(cursor);\n hints.style.left = (left = pos.left - offsetLeft) + \"px\";\n box = hints.getBoundingClientRect();\n }\n }\n }\n var overlapX = box.right - winW;\n if (overlapX > 0) {\n if (box.right - box.left > winW) {\n hints.style.width = (winW - 5) + \"px\";\n overlapX -= (box.right - box.left) - winW;\n }\n hints.style.left = (left = pos.left - overlapX - offsetLeft) + \"px\";\n }\n if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling)\n node.style.paddingRight = cm.display.nativeBarWidth + \"px\"\n\n cm.addKeyMap(this.keyMap = buildKeyMap(completion, {\n moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },\n setFocus: function(n) { widget.changeActive(n); },\n menuSize: function() { return widget.screenAmount(); },\n length: completions.length,\n close: function() { completion.close(); },\n pick: function() { widget.pick(); },\n data: data\n }));\n\n if (completion.options.closeOnUnfocus) {\n var closingOnBlur;\n cm.on(\"blur\", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });\n cm.on(\"focus\", this.onFocus = function() { clearTimeout(closingOnBlur); });\n }\n\n cm.on(\"scroll\", this.onScroll = function() {\n var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();\n var newTop = top + startScroll.top - curScroll.top;\n var point = newTop - (parentWindow.pageYOffset || (ownerDocument.documentElement || ownerDocument.body).scrollTop);\n if (!below) point += hints.offsetHeight;\n if (point <= editor.top || point >= editor.bottom) return completion.close();\n hints.style.top = newTop + \"px\";\n hints.style.left = (left + startScroll.left - curScroll.left) + \"px\";\n });\n\n CodeMirror.on(hints, \"dblclick\", function(e) {\n var t = getHintElement(hints, e.target || e.srcElement);\n if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}\n });\n\n CodeMirror.on(hints, \"click\", function(e) {\n var t = getHintElement(hints, e.target || e.srcElement);\n if (t && t.hintId != null) {\n widget.changeActive(t.hintId);\n if (completion.options.completeOnSingleClick) widget.pick();\n }\n });\n\n CodeMirror.on(hints, \"mousedown\", function() {\n setTimeout(function(){cm.focus();}, 20);\n });\n this.scrollToActive()\n\n CodeMirror.signal(data, \"select\", completions[this.selectedHint], hints.childNodes[this.selectedHint]);\n return true;\n }\n\n Widget.prototype = {\n close: function() {\n if (this.completion.widget != this) return;\n this.completion.widget = null;\n this.hints.parentNode.removeChild(this.hints);\n this.completion.cm.removeKeyMap(this.keyMap);\n\n var cm = this.completion.cm;\n if (this.completion.options.closeOnUnfocus) {\n cm.off(\"blur\", this.onBlur);\n cm.off(\"focus\", this.onFocus);\n }\n cm.off(\"scroll\", this.onScroll);\n },\n\n disable: function() {\n this.completion.cm.removeKeyMap(this.keyMap);\n var widget = this;\n this.keyMap = {Enter: function() { widget.picked = true; }};\n this.completion.cm.addKeyMap(this.keyMap);\n },\n\n pick: function() {\n this.completion.pick(this.data, this.selectedHint);\n },\n\n changeActive: function(i, avoidWrap) {\n if (i >= this.data.list.length)\n i = avoidWrap ? this.data.list.length - 1 : 0;\n else if (i < 0)\n i = avoidWrap ? 0 : this.data.list.length - 1;\n if (this.selectedHint == i) return;\n var node = this.hints.childNodes[this.selectedHint];\n if (node) node.className = node.className.replace(\" \" + ACTIVE_HINT_ELEMENT_CLASS, \"\");\n node = this.hints.childNodes[this.selectedHint = i];\n node.className += \" \" + ACTIVE_HINT_ELEMENT_CLASS;\n this.scrollToActive()\n CodeMirror.signal(this.data, \"select\", this.data.list[this.selectedHint], node);\n },\n\n scrollToActive: function() {\n var node = this.hints.childNodes[this.selectedHint]\n if (node.offsetTop < this.hints.scrollTop)\n this.hints.scrollTop = node.offsetTop - 3;\n else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)\n this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3;\n },\n\n screenAmount: function() {\n return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1;\n }\n };\n\n function applicableHelpers(cm, helpers) {\n if (!cm.somethingSelected()) return helpers\n var result = []\n for (var i = 0; i < helpers.length; i++)\n if (helpers[i].supportsSelection) result.push(helpers[i])\n return result\n }\n\n function fetchHints(hint, cm, options, callback) {\n if (hint.async) {\n hint(cm, callback, options)\n } else {\n var result = hint(cm, options)\n if (result && result.then) result.then(callback)\n else callback(result)\n }\n }\n\n function resolveAutoHints(cm, pos) {\n var helpers = cm.getHelpers(pos, \"hint\"), words\n if (helpers.length) {\n var resolved = function(cm, callback, options) {\n var app = applicableHelpers(cm, helpers);\n function run(i) {\n if (i == app.length) return callback(null)\n fetchHints(app[i], cm, options, function(result) {\n if (result && result.list.length > 0) callback(result)\n else run(i + 1)\n })\n }\n run(0)\n }\n resolved.async = true\n resolved.supportsSelection = true\n return resolved\n } else if (words = cm.getHelper(cm.getCursor(), \"hintWords\")) {\n return function(cm) { return CodeMirror.hint.fromList(cm, {words: words}) }\n } else if (CodeMirror.hint.anyword) {\n return function(cm, options) { return CodeMirror.hint.anyword(cm, options) }\n } else {\n return function() {}\n }\n }\n\n CodeMirror.registerHelper(\"hint\", \"auto\", {\n resolve: resolveAutoHints\n });\n\n CodeMirror.registerHelper(\"hint\", \"fromList\", function(cm, options) {\n var cur = cm.getCursor(), token = cm.getTokenAt(cur)\n var term, from = CodeMirror.Pos(cur.line, token.start), to = cur\n if (token.start < cur.ch && /\\w/.test(token.string.charAt(cur.ch - token.start - 1))) {\n term = token.string.substr(0, cur.ch - token.start)\n } else {\n term = \"\"\n from = cur\n }\n var found = [];\n for (var i = 0; i < options.words.length; i++) {\n var word = options.words[i];\n if (word.slice(0, term.length) == term)\n found.push(word);\n }\n\n if (found.length) return {list: found, from: from, to: to};\n });\n\n CodeMirror.commands.autocomplete = CodeMirror.showHint;\n\n var defaultOptions = {\n hint: CodeMirror.hint.auto,\n completeSingle: true,\n alignWithWord: true,\n closeCharacters: /[\\s()\\[\\]{};:>,]/,\n closeOnUnfocus: true,\n completeOnSingleClick: true,\n container: null,\n customKeys: null,\n extraKeys: null\n };\n\n CodeMirror.defineOption(\"hintOptions\", null);\n});\n","// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/LICENSE\n\n(function(mod) {\n if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n mod(require(\"../../lib/codemirror\"));\n else if (typeof define == \"function\" && define.amd) // AMD\n define([\"../../lib/codemirror\"], mod);\n else // Plain browser env\n mod(CodeMirror);\n})(function(CodeMirror) {\n \"use strict\";\n\n var noOptions = {};\n var nonWS = /[^\\s\\u00a0]/;\n var Pos = CodeMirror.Pos;\n\n function firstNonWS(str) {\n var found = str.search(nonWS);\n return found == -1 ? 0 : found;\n }\n\n CodeMirror.commands.toggleComment = function(cm) {\n cm.toggleComment();\n };\n\n CodeMirror.defineExtension(\"toggleComment\", function(options) {\n if (!options) options = noOptions;\n var cm = this;\n var minLine = Infinity, ranges = this.listSelections(), mode = null;\n for (var i = ranges.length - 1; i >= 0; i--) {\n var from = ranges[i].from(), to = ranges[i].to();\n if (from.line >= minLine) continue;\n if (to.line >= minLine) to = Pos(minLine, 0);\n minLine = from.line;\n if (mode == null) {\n if (cm.uncomment(from, to, options)) mode = \"un\";\n else { cm.lineComment(from, to, options); mode = \"line\"; }\n } else if (mode == \"un\") {\n cm.uncomment(from, to, options);\n } else {\n cm.lineComment(from, to, options);\n }\n }\n });\n\n // Rough heuristic to try and detect lines that are part of multi-line string\n function probablyInsideString(cm, pos, line) {\n return /\\bstring\\b/.test(cm.getTokenTypeAt(Pos(pos.line, 0))) && !/^[\\'\\\"\\`]/.test(line)\n }\n\n function getMode(cm, pos) {\n var mode = cm.getMode()\n return mode.useInnerComments === false || !mode.innerMode ? mode : cm.getModeAt(pos)\n }\n\n CodeMirror.defineExtension(\"lineComment\", function(from, to, options) {\n if (!options) options = noOptions;\n var self = this, mode = getMode(self, from);\n var firstLine = self.getLine(from.line);\n if (firstLine == null || probablyInsideString(self, from, firstLine)) return;\n\n var commentString = options.lineComment || mode.lineComment;\n if (!commentString) {\n if (options.blockCommentStart || mode.blockCommentStart) {\n options.fullLines = true;\n self.blockComment(from, to, options);\n }\n return;\n }\n\n var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1);\n var pad = options.padding == null ? \" \" : options.padding;\n var blankLines = options.commentBlankLines || from.line == to.line;\n\n self.operation(function() {\n if (options.indent) {\n var baseString = null;\n for (var i = from.line; i < end; ++i) {\n var line = self.getLine(i);\n var whitespace = line.slice(0, firstNonWS(line));\n if (baseString == null || baseString.length > whitespace.length) {\n baseString = whitespace;\n }\n }\n for (var i = from.line; i < end; ++i) {\n var line = self.getLine(i), cut = baseString.length;\n if (!blankLines && !nonWS.test(line)) continue;\n if (line.slice(0, cut) != baseString) cut = firstNonWS(line);\n self.replaceRange(baseString + commentString + pad, Pos(i, 0), Pos(i, cut));\n }\n } else {\n for (var i = from.line; i < end; ++i) {\n if (blankLines || nonWS.test(self.getLine(i)))\n self.replaceRange(commentString + pad, Pos(i, 0));\n }\n }\n });\n });\n\n CodeMirror.defineExtension(\"blockComment\", function(from, to, options) {\n if (!options) options = noOptions;\n var self = this, mode = getMode(self, from);\n var startString = options.blockCommentStart || mode.blockCommentStart;\n var endString = options.blockCommentEnd || mode.blockCommentEnd;\n if (!startString || !endString) {\n if ((options.lineComment || mode.lineComment) && options.fullLines != false)\n self.lineComment(from, to, options);\n return;\n }\n if (/\\bcomment\\b/.test(self.getTokenTypeAt(Pos(from.line, 0)))) return\n\n var end = Math.min(to.line, self.lastLine());\n if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end;\n\n var pad = options.padding == null ? \" \" : options.padding;\n if (from.line > end) return;\n\n self.operation(function() {\n if (options.fullLines != false) {\n var lastLineHasText = nonWS.test(self.getLine(end));\n self.replaceRange(pad + endString, Pos(end));\n self.replaceRange(startString + pad, Pos(from.line, 0));\n var lead = options.blockCommentLead || mode.blockCommentLead;\n if (lead != null) for (var i = from.line + 1; i <= end; ++i)\n if (i != end || lastLineHasText)\n self.replaceRange(lead + pad, Pos(i, 0));\n } else {\n self.replaceRange(endString, to);\n self.replaceRange(startString, from);\n }\n });\n });\n\n CodeMirror.defineExtension(\"uncomment\", function(from, to, options) {\n if (!options) options = noOptions;\n var self = this, mode = getMode(self, from);\n var end = Math.min(to.ch != 0 || to.line == from.line ? to.line : to.line - 1, self.lastLine()), start = Math.min(from.line, end);\n\n // Try finding line comments\n var lineString = options.lineComment || mode.lineComment, lines = [];\n var pad = options.padding == null ? \" \" : options.padding, didSomething;\n lineComment: {\n if (!lineString) break lineComment;\n for (var i = start; i <= end; ++i) {\n var line = self.getLine(i);\n var found = line.indexOf(lineString);\n if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1;\n if (found == -1 && nonWS.test(line)) break lineComment;\n if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment;\n lines.push(line);\n }\n self.operation(function() {\n for (var i = start; i <= end; ++i) {\n var line = lines[i - start];\n var pos = line.indexOf(lineString), endPos = pos + lineString.length;\n if (pos < 0) continue;\n if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length;\n didSomething = true;\n self.replaceRange(\"\", Pos(i, pos), Pos(i, endPos));\n }\n });\n if (didSomething) return true;\n }\n\n // Try block comments\n var startString = options.blockCommentStart || mode.blockCommentStart;\n var endString = options.blockCommentEnd || mode.blockCommentEnd;\n if (!startString || !endString) return false;\n var lead = options.blockCommentLead || mode.blockCommentLead;\n var startLine = self.getLine(start), open = startLine.indexOf(startString)\n if (open == -1) return false\n var endLine = end == start ? startLine : self.getLine(end)\n var close = endLine.indexOf(endString, end == start ? open + startString.length : 0);\n var insideStart = Pos(start, open + 1), insideEnd = Pos(end, close + 1)\n if (close == -1 ||\n !/comment/.test(self.getTokenTypeAt(insideStart)) ||\n !/comment/.test(self.getTokenTypeAt(insideEnd)) ||\n self.getRange(insideStart, insideEnd, \"\\n\").indexOf(endString) > -1)\n return false;\n\n // Avoid killing block comments completely outside the selection.\n // Positions of the last startString before the start of the selection, and the first endString after it.\n var lastStart = startLine.lastIndexOf(startString, from.ch);\n var firstEnd = lastStart == -1 ? -1 : startLine.slice(0, from.ch).indexOf(endString, lastStart + startString.length);\n if (lastStart != -1 && firstEnd != -1 && firstEnd + endString.length != from.ch) return false;\n // Positions of the first endString after the end of the selection, and the last startString before it.\n firstEnd = endLine.indexOf(endString, to.ch);\n var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch);\n lastStart = (firstEnd == -1 || almostLastStart == -1) ? -1 : to.ch + almostLastStart;\n if (firstEnd != -1 && lastStart != -1 && lastStart != to.ch) return false;\n\n self.operation(function() {\n self.replaceRange(\"\", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)),\n Pos(end, close + endString.length));\n var openEnd = open + startString.length;\n if (pad && startLine.slice(openEnd, openEnd + pad.length) == pad) openEnd += pad.length;\n self.replaceRange(\"\", Pos(start, open), Pos(start, openEnd));\n if (lead) for (var i = start + 1; i <= end; ++i) {\n var line = self.getLine(i), found = line.indexOf(lead);\n if (found == -1 || nonWS.test(line.slice(0, found))) continue;\n var foundEnd = found + lead.length;\n if (pad && line.slice(foundEnd, foundEnd + pad.length) == pad) foundEnd += pad.length;\n self.replaceRange(\"\", Pos(i, found), Pos(i, foundEnd));\n }\n });\n return true;\n });\n});\n","// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/LICENSE\n\n(function(mod) {\n if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n mod(require(\"../../lib/codemirror\"));\n else if (typeof define == \"function\" && define.amd) // AMD\n define([\"../../lib/codemirror\"], mod);\n else // Plain browser env\n mod(CodeMirror);\n})(function(CodeMirror) {\n CodeMirror.defineOption(\"placeholder\", \"\", function(cm, val, old) {\n var prev = old && old != CodeMirror.Init;\n if (val && !prev) {\n cm.on(\"blur\", onBlur);\n cm.on(\"change\", onChange);\n cm.on(\"swapDoc\", onChange);\n onChange(cm);\n } else if (!val && prev) {\n cm.off(\"blur\", onBlur);\n cm.off(\"change\", onChange);\n cm.off(\"swapDoc\", onChange);\n clearPlaceholder(cm);\n var wrapper = cm.getWrapperElement();\n wrapper.className = wrapper.className.replace(\" CodeMirror-empty\", \"\");\n }\n\n if (val && !cm.hasFocus()) onBlur(cm);\n });\n\n function clearPlaceholder(cm) {\n if (cm.state.placeholder) {\n cm.state.placeholder.parentNode.removeChild(cm.state.placeholder);\n cm.state.placeholder = null;\n }\n }\n function setPlaceholder(cm) {\n clearPlaceholder(cm);\n var elt = cm.state.placeholder = document.createElement(\"pre\");\n elt.style.cssText = \"height: 0; overflow: visible\";\n elt.style.direction = cm.getOption(\"direction\");\n elt.className = \"CodeMirror-placeholder CodeMirror-line-like\";\n var placeHolder = cm.getOption(\"placeholder\")\n if (typeof placeHolder == \"string\") placeHolder = document.createTextNode(placeHolder)\n elt.appendChild(placeHolder)\n cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild);\n }\n\n function onBlur(cm) {\n if (isEmpty(cm)) setPlaceholder(cm);\n }\n function onChange(cm) {\n var wrapper = cm.getWrapperElement(), empty = isEmpty(cm);\n wrapper.className = wrapper.className.replace(\" CodeMirror-empty\", \"\") + (empty ? \" CodeMirror-empty\" : \"\");\n\n if (empty) setPlaceholder(cm);\n else clearPlaceholder(cm);\n }\n\n function isEmpty(cm) {\n return (cm.lineCount() === 1) && (cm.getLine(0) === \"\");\n }\n});\n","// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: https://codemirror.net/LICENSE\n\n(function(mod) {\n if (typeof exports == \"object\" && typeof module == \"object\") // CommonJS\n mod(require(\"../../lib/codemirror\"));\n else if (typeof define == \"function\" && define.amd) // AMD\n define([\"../../lib/codemirror\"], mod);\n else // Plain browser env\n mod(CodeMirror);\n})(function(CodeMirror) {\n \"use strict\";\n var WRAP_CLASS = \"CodeMirror-activeline\";\n var BACK_CLASS = \"CodeMirror-activeline-background\";\n var GUTT_CLASS = \"CodeMirror-activeline-gutter\";\n\n CodeMirror.defineOption(\"styleActiveLine\", false, function(cm, val, old) {\n var prev = old == CodeMirror.Init ? false : old;\n if (val == prev) return\n if (prev) {\n cm.off(\"beforeSelectionChange\", selectionChange);\n clearActiveLines(cm);\n delete cm.state.activeLines;\n }\n if (val) {\n cm.state.activeLines = [];\n updateActiveLines(cm, cm.listSelections());\n cm.on(\"beforeSelectionChange\", selectionChange);\n }\n });\n\n function clearActiveLines(cm) {\n for (var i = 0; i < cm.state.activeLines.length; i++) {\n cm.removeLineClass(cm.state.activeLines[i], \"wrap\", WRAP_CLASS);\n cm.removeLineClass(cm.state.activeLines[i], \"background\", BACK_CLASS);\n cm.removeLineClass(cm.state.activeLines[i], \"gutter\", GUTT_CLASS);\n }\n }\n\n function sameArray(a, b) {\n if (a.length != b.length) return false;\n for (var i = 0; i < a.length; i++)\n if (a[i] != b[i]) return false;\n return true;\n }\n\n function updateActiveLines(cm, ranges) {\n var active = [];\n for (var i = 0; i < ranges.length; i++) {\n var range = ranges[i];\n var option = cm.getOption(\"styleActiveLine\");\n if (typeof option == \"object\" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty())\n continue\n var line = cm.getLineHandleVisualStart(range.head.line);\n if (active[active.length - 1] != line) active.push(line);\n }\n if (sameArray(cm.state.activeLines, active)) return;\n cm.operation(function() {\n clearActiveLines(cm);\n for (var i = 0; i < active.length; i++) {\n cm.addLineClass(active[i], \"wrap\", WRAP_CLASS);\n cm.addLineClass(active[i], \"background\", BACK_CLASS);\n cm.addLineClass(active[i], \"gutter\", GUTT_CLASS);\n }\n cm.state.activeLines = active;\n });\n }\n\n function selectionChange(cm, sel) {\n updateActiveLines(cm, sel.ranges);\n }\n});\n","import CodeMirror, { Editor, Position } from \"codemirror\";\n\nCodeMirror.registerHelper(\n \"fold\",\n \"beancount\",\n (cm: Editor, start: Position) => {\n const maxDepth = 100;\n\n function headerLevel(lineNo: number) {\n const line = cm.getDoc().getLine(lineNo);\n const match = line && line.match(/^\\*+/);\n if (match) {\n return match[0].length;\n }\n return maxDepth;\n }\n\n const level = headerLevel(start.line);\n\n if (level === maxDepth) {\n return undefined;\n }\n\n const doc = cm.getDoc();\n const lastLineNo = doc.lastLine();\n let end = start.line;\n\n while (end < lastLineNo) {\n if (headerLevel(end + 1) <= level) {\n break;\n }\n end += 1;\n }\n\n return {\n from: new CodeMirror.Pos(start.line, doc.getLine(start.line).length),\n to: new CodeMirror.Pos(end, doc.getLine(end).length),\n };\n }\n);\n","import CodeMirror, { Position } from \"codemirror\";\nimport { fuzzytest } from \"../helpers\";\n\nexport function getCurrentWord(cursor: Position, line: string) {\n return line.slice(0, cursor.ch).match(/(\\S*)$/)![0];\n}\n\nexport function fuzzyMatch(\n cursor: Position,\n currentWord: string,\n completions: string[]\n) {\n const search = currentWord.toLowerCase();\n return {\n list: completions.filter(completion => fuzzytest(search, completion)),\n from: new CodeMirror.Pos(cursor.line, cursor.ch - currentWord.length),\n to: cursor,\n };\n}\n","import CodeMirror, { Editor } from \"codemirror\";\n\nimport { fuzzyMatch, getCurrentWord } from \"./helpers\";\nimport { favaAPI } from \"../stores\";\n\nconst completionSources = {\n undatedDirectives: [\"option\", \"plugin\", \"include\"],\n datedDirectives: [\n \"open\",\n \"close\",\n \"commodity\",\n \"balance\",\n \"pad\",\n \"note\",\n \"document\",\n \"price\",\n \"event\",\n \"query\",\n ],\n};\n\nconst directiveCompletions: Record<\n string,\n Array<\"accounts\" | \"currencies\" | null>\n> = {\n open: [\"accounts\", \"currencies\"],\n close: [\"accounts\"],\n commodity: [\"currencies\"],\n balance: [\"accounts\", null, \"currencies\"],\n pad: [\"accounts\", \"accounts\"],\n note: [\"accounts\"],\n document: [\"accounts\"],\n price: [\"currencies\", null, \"currencies\"],\n};\n\nCodeMirror.registerHelper(\"hint\", \"beancount\", (cm: Editor) => {\n const doc = cm.getDoc();\n const cursor = doc.getCursor();\n const line = doc.getLine(cursor.line);\n const token = cm.getTokenAt(cursor);\n const currentCharacter = line[cursor.ch - 1];\n const currentWord = getCurrentWord(cursor, line);\n\n // If '#' or '^' has just been typed, there won't be a tag or link token yet\n if (currentCharacter === \"#\" || currentCharacter === \"^\") {\n const list = currentCharacter === \"#\" ? favaAPI.tags : favaAPI.links;\n return {\n list,\n from: cursor,\n to: cursor,\n };\n }\n\n if (token.type === \"tag\" || token.type === \"link\") {\n const list = token.type === \"tag\" ? favaAPI.tags : favaAPI.links;\n return {\n list: list.filter(d => d.startsWith(currentWord.slice(1))),\n from: new CodeMirror.Pos(cursor.line, token.start + 1),\n to: new CodeMirror.Pos(cursor.line, token.end),\n };\n }\n\n // directives at the start of the line\n if (currentWord === line && line.length > 0) {\n return {\n list: completionSources.undatedDirectives.filter(d =>\n d.startsWith(currentWord)\n ),\n from: new CodeMirror.Pos(cursor.line, 0),\n to: cursor,\n };\n }\n\n const lineTokens = cm.getLineTokens(cursor.line);\n\n if (lineTokens.length > 0) {\n const startCurrentWord = cursor.ch - currentWord.length;\n const previousTokens = lineTokens.filter(d => d.end <= startCurrentWord);\n\n // complete accounts for indented lines\n if (lineTokens[0].type === \"whitespace\") {\n if (previousTokens.length === 1) {\n return fuzzyMatch(cursor, currentWord, favaAPI.accounts);\n }\n }\n\n // dated directives\n if (lineTokens[0].type === \"date\") {\n // date whitespace -> complete directives\n if (previousTokens.length === 2) {\n return {\n list: completionSources.datedDirectives.filter(d =>\n d.startsWith(currentWord)\n ),\n from: new CodeMirror.Pos(cursor.line, cursor.ch - currentWord.length),\n to: cursor,\n };\n }\n\n // Ignore negative sign from previousTokens\n const tokenLength = previousTokens.filter(t => t.type != null).length;\n if (tokenLength % 2 === 0) {\n const directiveType = previousTokens[2].string;\n if (directiveType in directiveCompletions) {\n const complType =\n directiveCompletions[directiveType][tokenLength / 2 - 2];\n if (complType) {\n return fuzzyMatch(cursor, currentWord, favaAPI[complType]);\n }\n }\n }\n }\n }\n\n return {\n list: [],\n };\n});\n","/* eslint-disable no-useless-escape */\n// To keep the regular expressions in sync with Beancount, they might contain\n// some superfluous escape characters.\n\nimport CodeMirror from \"codemirror\";\n\n// The rules should mirror `parser/lexel.l` in beancount\nCodeMirror.defineSimpleMode(\"beancount\", {\n start: [\n {\n regex: /\\*.*/,\n token: \"comment section\",\n sol: true,\n },\n {\n regex: /[#*;].*/,\n token: \"comment\",\n sol: true,\n },\n {\n regex: /;.*/,\n token: \"comment\",\n },\n {\n regex: /(query)(\\s*)(\"[^\"]*\")(\\s*)(\")/,\n token: [\"directive\", \"\", \"string\", \"\", \"string\"],\n mode: {\n spec: \"beancount-query\",\n end: /\"/,\n },\n },\n {\n regex: /\"(?:[^\\\\]|\\\\.)*?\"/,\n token: \"string\",\n },\n {\n regex: /@|@@|{|}/,\n token: \"bracket\",\n },\n {\n regex: /\\s+/,\n token: \"whitespace\",\n },\n {\n regex: /#[A-Za-z0-9\\-_\\/.]+/,\n token: \"tag\",\n },\n {\n regex: /[A-Z][A-Z0-9'\\._\\-]{0,22}[A-Z0-9]/,\n token: \"commodity keyword\",\n },\n {\n regex: /TRUE|FALSE/,\n token: \"bool atom\",\n },\n {\n regex: /(?:[A-Z][A-Za-z0-9\\-]+)(?::[A-Z][A-Za-z0-9\\-]*)+/,\n token: \"account\",\n },\n {\n regex: /[*!&#?%PSTCURM]|txn/,\n token: \"directive transaction\",\n },\n // other dated directives\n {\n regex: /balance|open|close|commodity|pad|event|custom|price|note|document/,\n token: \"directive\",\n },\n // undated directives\n {\n regex: /pushtag|poptag|pushmeta|popmeta|option|plugin|include/,\n token: \"directive\",\n sol: true,\n },\n {\n regex: /[0-9]{4,}[\\-\\/][0-9]+[\\-\\/][0-9]+/,\n token: \"date\",\n },\n {\n regex: /(?:[0-9]+|[0-9][0-9,]+[0-9])(?:\\.[0-9]*)?/,\n token: \"number\",\n },\n {\n regex: /\\^[A-Za-z0-9\\-_\\/.]+/,\n token: \"attribute\",\n },\n {\n regex: /[a-z][a-za-z0-9\\-_]+:/,\n token: \"meta\",\n },\n ],\n});\n","export default {\n columns: [\n \"account\",\n \"balance\",\n \"change\",\n \"cost_currency\",\n \"cost_date\",\n \"cost_label\",\n \"cost_number\",\n \"currency\",\n \"date\",\n \"day\",\n \"description\",\n \"filename\",\n \"flag\",\n \"id\",\n \"lineno\",\n \"links\",\n \"location\",\n \"month\",\n \"narration\",\n \"number\",\n \"other_accounts\",\n \"payee\",\n \"position\",\n \"posting_flag\",\n \"price\",\n \"tags\",\n \"type\",\n \"weight\",\n \"year\",\n ],\n functions: [\n \"abs\",\n \"account_sortkey\",\n \"any_meta\",\n \"close_date\",\n \"coalesce\",\n \"commodity\",\n \"commodity_meta\",\n \"convert\",\n \"cost\",\n \"count\",\n \"currency\",\n \"currency_meta\",\n \"date\",\n \"date_add\",\n \"date_diff\",\n \"day\",\n \"entry_meta\",\n \"filter_currency\",\n \"findfirst\",\n \"first\",\n \"getitem\",\n \"getprice\",\n \"grep\",\n \"grepn\",\n \"joinstr\",\n \"last\",\n \"leaf\",\n \"length\",\n \"max\",\n \"maxwidth\",\n \"meta\",\n \"min\",\n \"month\",\n \"neg\",\n \"number\",\n \"only\",\n \"open_date\",\n \"open_meta\",\n \"parent\",\n \"possign\",\n \"quarter\",\n \"root\",\n \"safediv\",\n \"str\",\n \"subst\",\n \"sum\",\n \"today\",\n \"units\",\n \"value\",\n \"weekday\",\n \"year\",\n \"ymonth\",\n ],\n keywords: [\n \"and\",\n \"as\",\n \"asc\",\n \"at\",\n \"balances\",\n \"by\",\n \"clear\",\n \"close\",\n \"desc\",\n \"distinct\",\n \"errors\",\n \"explain\",\n \"false\",\n \"flatten\",\n \"from\",\n \"group\",\n \"having\",\n \"in\",\n \"journal\",\n \"limit\",\n \"not\",\n \"null\",\n \"on\",\n \"open\",\n \"or\",\n \"order\",\n \"pivot\",\n \"print\",\n \"reload\",\n \"run\",\n \"select\",\n \"true\",\n \"where\",\n ],\n};\n","import CodeMirror, { Editor } from \"codemirror\";\n\nimport { fuzzyMatch, getCurrentWord } from \"./helpers\";\nimport bqlGrammar from \"./bql-grammar\";\n\nconst { columns, functions, keywords } = bqlGrammar;\n\nconst functionCompletions = functions.map((f: string) => `${f}(`);\nconst commands = [\"select\"];\n\nCodeMirror.registerHelper(\"hint\", \"beancount-query\", (cm: Editor) => {\n const doc = cm.getDoc();\n const cursor = doc.getCursor();\n const line = doc.getLine(cursor.line);\n const currentWord = getCurrentWord(cursor, line);\n\n // keywords at the start of the line\n if (currentWord === line) {\n return {\n list: commands.filter(d => d.startsWith(currentWord)),\n from: new CodeMirror.Pos(cursor.line, 0),\n to: cursor,\n };\n }\n\n return fuzzyMatch(\n cursor,\n currentWord,\n columns.concat(functionCompletions, keywords)\n );\n});\n","/* eslint-disable no-useless-escape */\n// To keep the regular expressions in sync with Beancount, they might contain\n// some superfluous escape characters.\n\nimport CodeMirror from \"codemirror\";\n\n// Import the autogenerated (by a script in the 'contrib' directory) lists\nimport bqlGrammar from \"./bql-grammar\";\n\nconst { columns, functions, keywords } = bqlGrammar;\n\n// This should match the grammar defined in Beancount (`query/query_parser.py`).\nCodeMirror.defineSimpleMode(\"beancount-query\", {\n start: [\n {\n regex: new RegExp(`(?=^|\\\\s)(${keywords.join(\"|\")})(?=\\\\s|$)`, \"i\"),\n token: \"keyword\",\n },\n {\n regex: /(\\\"[^\\\"]*\\\"|\\'[^\\']*\\')/,\n token: \"string\",\n },\n {\n regex: /(?:\\#(?:\\\"[^\\\"]*\\\"|\\'[^\\']*\\')|\\d\\d\\d\\d-\\d\\d-\\d\\d)/,\n token: \"date\",\n },\n {\n regex: /[-+]?([0-9]+\\.[0-9]*|[0-9]*\\.[0-9]+)/,\n token: \"number\",\n },\n {\n regex: /[-+]?[0-9]+/,\n token: \"number\",\n },\n {\n regex: new RegExp(`(${columns.join(\"|\")})(?=\\\\)|\\\\s|,|$)`, \"i\"),\n token: \"variable-2\",\n },\n {\n regex: new RegExp(`(${functions.join(\"|\")})(?=\\\\()`, \"i\"),\n token: \"variable-3\",\n },\n ],\n});\n","import CodeMirror, { Editor, EditorFromTextArea } from \"codemirror\";\nimport Mousetrap from \"mousetrap\";\n\nimport \"codemirror/addon/mode/simple\";\n\n// search\nimport \"codemirror/addon/dialog/dialog\";\nimport \"codemirror/addon/search/searchcursor\";\nimport \"codemirror/addon/search/search\";\n\n// print margin\nimport \"codemirror/addon/display/rulers\";\n\n// trailing whitespace\nimport \"codemirror/addon/edit/trailingspace\";\n\n// folding\nimport \"codemirror/addon/fold/foldcode\";\nimport \"codemirror/addon/fold/foldgutter\";\n\n// auto-complete\nimport \"codemirror/addon/hint/show-hint\";\n\n// commenting\nimport \"codemirror/addon/comment/comment\";\n\n// placeholder\nimport \"codemirror/addon/display/placeholder\";\n\n// highlight line\nimport \"codemirror/addon/selection/active-line\";\n\nimport \"./codemirror/fold-beancount\";\nimport \"./codemirror/hint-beancount\";\nimport \"./codemirror/mode-beancount\";\n\nimport \"./codemirror/hint-query\";\nimport \"./codemirror/mode-query\";\n\nimport { select, selectAll, putAPI } from \"./helpers\";\nimport e from \"./events\";\nimport router from \"./router\";\nimport { notify } from \"./notifications\";\nimport { closeOverlay, favaAPI } from \"./stores\";\n\ninterface SimpleModeRule {\n regex: string | RegExp;\n token: string | string[] | null;\n sol?: boolean;\n mode?: { spec: string; end: string | RegExp };\n}\n\ndeclare module \"codemirror\" {\n function defineSimpleMode(\n name: string,\n config: Record\n ): void;\n interface EditorConfiguration {\n // defined in the edit/trailingspace addon\n showTrailingSpace?: boolean;\n // defined in the display/rulers addon\n rulers?: {\n column: number;\n lineStyle: string;\n }[];\n favaSaveButton?: HTMLButtonElement;\n }\n interface CommandActions {\n favaSave(editor: EditorFromTextArea): void;\n favaFormat(editor: Editor): void;\n favaToggleComment(editor: Editor): void;\n favaCenterCursor(editor: Editor): void;\n favaJumpToMarker(editor: Editor): void;\n // defined in the hint/show-hint addon\n autocomplete(\n editor: Editor,\n getHints: undefined,\n options: { completeSingle: boolean }\n ): void;\n }\n}\n\nexport { CodeMirror };\n\n// This handles saving in both the main and the overlaid entry editors.\nCodeMirror.commands.favaSave = (cm: EditorFromTextArea) => {\n const button = cm.getOption(\"favaSaveButton\");\n if (!button) {\n return;\n }\n\n const buttonText = button.textContent;\n button.disabled = true;\n button.textContent = button.getAttribute(\"data-progress-content\");\n\n putAPI(\"source\", {\n file_path: button.getAttribute(\"data-filename\"),\n entry_hash: button.getAttribute(\"data-entry-hash\"),\n source: cm.getValue(),\n sha256sum: cm.getTextArea().getAttribute(\"data-sha256sum\"),\n })\n .then(\n data => {\n cm.focus();\n cm.getTextArea().setAttribute(\"data-sha256sum\", data);\n e.trigger(\"file-modified\");\n // Reload the page if an entry was changed.\n if (button.getAttribute(\"data-entry-hash\")) {\n router.reload();\n closeOverlay();\n }\n },\n error => {\n notify(error, \"error\");\n }\n )\n .then(() => {\n cm.getDoc().markClean();\n button.textContent = buttonText;\n });\n};\n\nCodeMirror.commands.favaFormat = (cm: Editor) => {\n putAPI(\"format_source\", { source: cm.getValue() }).then(\n data => {\n const scrollPosition = cm.getScrollInfo().top;\n cm.setValue(data);\n cm.scrollTo(null, scrollPosition);\n },\n error => {\n notify(error, \"error\");\n }\n );\n};\n\nCodeMirror.commands.favaToggleComment = (cm: Editor) => {\n const doc = cm.getDoc();\n const args = {\n from: doc.getCursor(\"start\"),\n to: doc.getCursor(\"end\"),\n options: { lineComment: \";\" },\n };\n if (!cm.uncomment(args.from, args.to, args.options)) {\n cm.lineComment(args.from, args.to, args.options);\n }\n};\n\nCodeMirror.commands.favaCenterCursor = (cm: Editor) => {\n const { top } = cm.cursorCoords(true, \"local\");\n const height = cm.getScrollInfo().clientHeight;\n cm.scrollTo(null, top - height / 2);\n};\n\nCodeMirror.commands.favaJumpToMarker = (cm: Editor) => {\n const doc = cm.getDoc();\n const cursor = cm.getSearchCursor(\"FAVA-INSERT-MARKER\");\n\n if (cursor.findNext()) {\n cm.focus();\n doc.setCursor(cursor.from());\n cm.execCommand(\"goLineUp\");\n cm.execCommand(\"favaCenterCursor\");\n } else {\n doc.setCursor(doc.lastLine(), 0);\n }\n};\n\n// If the given key should be ignored for autocompletion\nexport function ignoreKey(key: string) {\n switch (key) {\n case \"ArrowDown\":\n case \"ArrowUp\":\n case \"ArrowLeft\":\n case \"ArrowRight\":\n case \"PageDown\":\n case \"PageUp\":\n case \"Home\":\n case \"End\":\n case \"Escape\":\n case \"Enter\":\n case \"Alt\":\n case \"Control\":\n case \"Meta\":\n case \"Shift\":\n case \"CapsLock\":\n return true;\n default:\n return false;\n }\n}\n\n// Initialize read-only editors\nfunction initReadOnlyEditors() {\n selectAll(\"textarea.editor-readonly\").forEach(el => {\n CodeMirror.fromTextArea(el as HTMLTextAreaElement, {\n mode: \"beancount\",\n readOnly: true,\n });\n });\n}\n\nconst sourceEditorOptions: CodeMirror.EditorConfiguration = {\n mode: \"beancount\",\n indentUnit: 4,\n lineNumbers: true,\n foldGutter: true,\n showTrailingSpace: true,\n styleActiveLine: true,\n gutters: [\"CodeMirror-linenumbers\", \"CodeMirror-foldgutter\"],\n extraKeys: {\n \"Ctrl-Space\": \"autocomplete\",\n \"Ctrl-S\": \"favaSave\",\n \"Cmd-S\": \"favaSave\",\n \"Ctrl-D\": \"favaFormat\",\n \"Cmd-D\": \"favaFormat\",\n \"Ctrl-Y\": \"favaToggleComment\",\n \"Cmd-Y\": \"favaToggleComment\",\n Tab: (cm: Editor) => {\n if (cm.getDoc().somethingSelected()) {\n cm.execCommand(\"indentMore\");\n } else {\n cm.execCommand(\"insertSoftTab\");\n }\n },\n },\n};\n\nlet activeEditor: Editor | null = null;\n// Init source editor.\nexport default function initSourceEditor(name: string) {\n if (favaAPI.favaOptions[\"currency-column\"]) {\n sourceEditorOptions.rulers = [\n {\n column: favaAPI.favaOptions[\"currency-column\"] - 1,\n lineStyle: \"dotted\",\n },\n ];\n }\n\n const sourceEditorTextarea = select(name) as HTMLTextAreaElement;\n if (!sourceEditorTextarea) {\n return;\n }\n\n const editor = CodeMirror.fromTextArea(\n sourceEditorTextarea,\n sourceEditorOptions\n );\n if (name === \"#source-editor\") {\n activeEditor = editor;\n }\n const saveButton = select(`${name}-submit`) as HTMLButtonElement;\n editor.setOption(\"favaSaveButton\", saveButton);\n\n editor.on(\"changes\", (cm: Editor) => {\n saveButton.disabled = cm.getDoc().isClean();\n });\n\n editor.on(\"keyup\", (cm: Editor, event: Event) => {\n if (\n !cm.state.completionActive &&\n !ignoreKey((event as KeyboardEvent).key)\n ) {\n CodeMirror.commands.autocomplete(cm, undefined, {\n completeSingle: false,\n });\n }\n });\n const line = parseInt(\n new URLSearchParams(window.location.search).get(\"line\") || \"0\",\n 10\n );\n if (line > 0) {\n editor.getDoc().setCursor(line - 1, 0);\n editor.execCommand(\"favaCenterCursor\");\n } else {\n editor.execCommand(\"favaJumpToMarker\");\n }\n\n // keybindings when the focus is outside the editor\n Mousetrap.bind([\"ctrl+s\", \"meta+s\"], event => {\n event.preventDefault();\n editor.execCommand(\"favaSave\");\n });\n\n Mousetrap.bind([\"ctrl+d\", \"meta+d\"], event => {\n event.preventDefault();\n editor.execCommand(\"favaFormat\");\n });\n\n // Run editor commands with buttons in editor menu.\n selectAll(`${name}-form button`).forEach(button => {\n const command = button.getAttribute(\"data-command\");\n if (command) {\n button.addEventListener(\"click\", event => {\n event.preventDefault();\n event.stopImmediatePropagation();\n editor.execCommand(command);\n });\n }\n });\n}\n\ne.on(\"page-loaded\", () => {\n initReadOnlyEditors();\n initSourceEditor(\"#source-editor\");\n});\n\nconst leaveMessage =\n \"There are unsaved changes. Are you sure you want to leave?\";\n\ne.on(\"navigate\", (state: { interrupt?: boolean }) => {\n if (activeEditor) {\n if (!activeEditor.getDoc().isClean()) {\n // eslint-disable-next-line no-alert\n const leave = window.confirm(leaveMessage);\n if (!leave) {\n state.interrupt = true;\n } else {\n activeEditor = null;\n }\n } else {\n activeEditor = null;\n }\n }\n});\n\nwindow.addEventListener(\"beforeunload\", event => {\n if (activeEditor && !activeEditor.getDoc().isClean()) {\n event.returnValue = leaveMessage;\n }\n});\n","import { select, selectAll, delegate } from \"./helpers\";\nimport e from \"./events\";\nimport router from \"./router\";\nimport { filters } from \"./stores\";\n\nfunction addFilter(value: string) {\n filters.update(fs => {\n if (fs.filter) {\n return {\n ...fs,\n filter: `${fs.filter} ${value}`,\n };\n }\n return {\n ...fs,\n filter: value,\n };\n });\n}\n\ne.on(\"page-loaded\", () => {\n const journal = select(\".journal\");\n if (!journal) {\n return;\n }\n\n delegate(journal, \"click\", \"li\", event => {\n if (!event.target) {\n return;\n }\n const target = event.target as HTMLElement;\n if (target.tagName === \"A\") {\n return;\n }\n\n if (target.className === \"tag\" || target.className === \"link\") {\n // Filter for tags and links when clicking on them.\n addFilter(target.innerText);\n } else if (target.className === \"payee\") {\n // Filter for payees when clicking on them.\n addFilter(`payee:\"${target.innerText}\"`);\n } else if (target.tagName === \"DD\") {\n // Filter for metadata when clicking on the value.\n addFilter(\n ` ${(target.previousElementSibling as HTMLElement).innerText}\"${\n target.innerText\n }\"`\n );\n } else if (target.closest(\".indicators\")) {\n // Toggle postings and metadata by clicking on indicators.\n const entry = target.closest(\".transaction\");\n if (entry) {\n entry.classList.toggle(\"show-postings\");\n }\n }\n });\n\n // Toggle entries with buttons.\n selectAll(\"#entry-filters button\").forEach(button => {\n button.addEventListener(\"click\", () => {\n const type = button.getAttribute(\"data-type\");\n const shouldShow = button.classList.contains(\"inactive\");\n\n button.classList.toggle(\"inactive\", !shouldShow);\n if (type === \"transaction\" || type === \"custom\" || type === \"document\") {\n selectAll(`#entry-filters .${type}-toggle`).forEach(el => {\n el.classList.toggle(\"inactive\", !shouldShow);\n });\n }\n\n journal.classList.toggle(`show-${type}`, shouldShow);\n\n // Modify get params\n const filterShow: string[] = [];\n selectAll(\"#entry-filters button\").forEach(el => {\n const datatype = el.getAttribute(\"data-type\");\n if (datatype && !el.classList.contains(\"inactive\")) {\n filterShow.push(datatype);\n }\n });\n\n const url = new URL(window.location.href);\n url.searchParams.delete(\"show\");\n filterShow.forEach(filter => {\n url.searchParams.append(\"show\", filter);\n });\n router.navigate(url.toString(), false);\n });\n });\n});\n","import Mousetrap from \"mousetrap\";\n\nimport { select, selectAll, once } from \"./helpers\";\nimport e from \"./events\";\nimport { closeOverlay } from \"./stores\";\n\nfunction click(selector: string) {\n const element = select(selector);\n if (element && element instanceof HTMLElement) {\n element.click();\n }\n}\n\ne.on(\"page-loaded\", () => {\n selectAll(\"[data-key]\").forEach(element => {\n const key = element.getAttribute(\"data-key\");\n if (key !== null) {\n Mousetrap.bind(\n key,\n () => {\n const tag = element.tagName;\n if (tag === \"BUTTON\" || tag === \"A\") {\n (element as HTMLButtonElement | HTMLAnchorElement).click();\n } else if (tag === \"INPUT\") {\n (element as HTMLInputElement).focus();\n }\n },\n \"keyup\"\n );\n }\n });\n});\n\n// Add a tooltip showing the keyboard shortcut over the target element.\nfunction showTooltip(target: HTMLElement) {\n const tooltip = document.createElement(\"div\");\n tooltip.className = \"keyboard-tooltip\";\n tooltip.innerHTML = target.getAttribute(\"data-key\") || \"\";\n document.body.appendChild(tooltip);\n const parentCoords = target.getBoundingClientRect();\n // Padded 10px to the left if there is space or centered otherwise\n const left =\n parentCoords.left +\n Math.min((target.offsetWidth - tooltip.offsetWidth) / 2, 10);\n const top =\n parentCoords.top + (target.offsetHeight - tooltip.offsetHeight) / 2;\n tooltip.style.left = `${left}px`;\n tooltip.style.top = `${top + window.pageYOffset}px`;\n}\n\n// Show all keyboard shortcut tooltips.\nfunction showTooltips() {\n const reloadButton = select(\"#reload-page\");\n if (reloadButton) {\n reloadButton.classList.remove(\"hidden\");\n }\n selectAll(\"[data-key]\").forEach(el => {\n showTooltip(el as HTMLElement);\n });\n}\n\n// Remove all keyboard shortcut tooltips.\nfunction removeTooltips() {\n const reloadButton = select(\"#reload-page\");\n if (reloadButton) {\n reloadButton.classList.add(\"hidden\");\n }\n selectAll(\".keyboard-tooltip\").forEach(tooltip => {\n tooltip.remove();\n });\n}\n\ne.on(\"page-init\", () => {\n Mousetrap.bind(\"?\", () => {\n removeTooltips();\n showTooltips();\n once(document, \"mousedown\", () => {\n removeTooltips();\n });\n });\n\n Mousetrap.bind(\"esc\", () => {\n closeOverlay();\n removeTooltips();\n });\n\n // Charts\n Mousetrap.bind(\"c\", () => {\n const selected = select(\".chart-labels .selected\");\n\n if (selected && selected.nextElementSibling) {\n (selected.nextElementSibling as HTMLLabelElement).click();\n } else {\n click(\".chart-labels label:first-child\");\n }\n });\n Mousetrap.bind(\"C\", () => {\n const selected = select(\".chart-labels .selected\");\n\n if (selected && selected.previousElementSibling) {\n (selected.previousElementSibling as HTMLLabelElement).click();\n } else {\n click(\".chart-labels label:last-child\");\n }\n });\n});\n","/**\n * This script updates the links and error count in the sidebar as well as\n * toggling the sidebar on mobile.\n */\n\nimport { select, selectAll, fetchAPI } from \"./helpers\";\nimport { favaAPI } from \"./stores\";\nimport { number } from \"./validation\";\nimport e from \"./events\";\n\nfunction initSidebar() {\n selectAll(\"aside a\").forEach(el => {\n el.classList.remove(\"selected\");\n const href = el.getAttribute(\"href\");\n if (href && href.includes(window.location.pathname)) {\n el.classList.add(\"selected\");\n }\n });\n select(\"aside li.error\")!.classList.toggle(\"hidden\", favaAPI.errors === 0);\n select(\"aside li.error span\")!.innerHTML = `${favaAPI.errors}`;\n}\n\ne.on(\"page-init\", () => {\n const asideButton = select(\"#aside-button\")!;\n asideButton.addEventListener(\"click\", () => {\n select(\"aside\")!.classList.toggle(\"active\");\n asideButton.classList.toggle(\"active\");\n });\n});\n\ne.on(\"page-loaded\", () => {\n initSidebar();\n});\n\ne.on(\"file-modified\", async () => {\n const errors = await fetchAPI(\"errors\");\n favaAPI.errors = number(errors);\n initSidebar();\n});\n","/**\n * Sorting of tables and the journal.\n *\n * Only clicking on headers that have a data-sort attribute will have an\n * effect. The currently supported values for `data-sort` are:\n *\n * - 'string': Case-insensitive string comparison.\n * - 'num': Clean and parse to float.\n */\n\nimport { select, selectAll } from \"./helpers\";\nimport e from \"./events\";\n\nfunction parseNumber(num: string): number {\n const cleaned = num.replace(/[^\\-?0-9.]/g, \"\");\n const n = parseFloat(cleaned);\n return Number.isNaN(n) ? 0 : n;\n}\n\nfunction stringComparator(A: string, B: string): number {\n const a = A.toLowerCase();\n const b = B.toLowerCase();\n\n if (a === b) {\n return 0;\n }\n return a < b ? -1 : 1;\n}\nfunction numComparator(a: string, b: string): number {\n return parseNumber(a) - parseNumber(b);\n}\n\ntype SortOrder = \"desc\" | \"asc\";\n\n/**\n * Obtain the value to sort by for an element.\n */\nfunction getValue(el: HTMLElement): string {\n return el.getAttribute(\"data-sort-value\") || el.textContent || el.innerText;\n}\n\n/**\n * Generate a sort function for a given comparison type, using a getter\n */\nexport function sortFunc(\n type: string | null,\n order: SortOrder,\n getter: (e: T) => string\n): (a: T, b: T) => number {\n const comparator = type === \"num\" ? numComparator : stringComparator;\n function func(a: T, b: T): number {\n return (order === \"asc\" ? 1 : -1) * comparator(getter(a), getter(b));\n }\n return func;\n}\n\n/**\n * Sort elements contained in a given parent element.\n */\nfunction sortElements(\n parent: Element,\n elements: T[],\n selector: (e: T) => C,\n order: SortOrder,\n type: string | null\n): void {\n const sortFunction = sortFunc(type, order, (a: T) => getValue(selector(a)));\n const fragment = document.createDocumentFragment();\n elements.sort(sortFunction).forEach(el => {\n fragment.appendChild(el);\n });\n parent.appendChild(fragment);\n}\n\n/**\n * Obtain the sort order for the row from the row header\n */\nfunction getSortOrder(headerElement: Element): SortOrder {\n if (!headerElement.getAttribute(\"data-order\")) {\n return headerElement.getAttribute(\"data-sort-default\") === \"desc\"\n ? \"desc\"\n : \"asc\";\n }\n return headerElement.getAttribute(\"data-order\") === \"asc\" ? \"desc\" : \"asc\";\n}\n\nfunction sortableJournal(ol: HTMLOListElement): void {\n const head = select(\".head\", ol);\n if (!head) {\n return;\n }\n const headers = selectAll(\"span[data-sort]\", head);\n\n headers.forEach(header => {\n header.addEventListener(\"click\", () => {\n const order = getSortOrder(header);\n const type = header.getAttribute(\"data-sort\");\n const headerClass = header.classList[0];\n\n // update sort order\n headers.forEach(el => {\n el.removeAttribute(\"data-order\");\n });\n header.setAttribute(\"data-order\", order);\n\n sortElements(\n ol,\n [].slice.call(ol.children, 1),\n function selector(li: HTMLLIElement): HTMLElement {\n return li.querySelector(`.${headerClass}`) as HTMLElement;\n },\n order,\n type\n );\n });\n });\n}\n\nfunction sortableTable(table: HTMLTableElement): void {\n const head = table.tHead;\n const body = table.tBodies.item(0);\n if (!head || !body) {\n return;\n }\n const headers = selectAll(\"th[data-sort]\", head);\n\n headers.forEach(header => {\n header.addEventListener(\"click\", () => {\n const order = getSortOrder(header);\n const type = header.getAttribute(\"data-sort\");\n const index = headers.indexOf(header);\n\n // update sort order\n headers.forEach(el => {\n el.removeAttribute(\"data-order\");\n });\n header.setAttribute(\"data-order\", order);\n\n sortElements(\n body,\n selectAll(\"tr\", body) as HTMLTableRowElement[],\n function selector(tr: HTMLTableRowElement): HTMLTableDataCellElement {\n // eslint-disable-next-line\n return tr.cells.item(index)!;\n },\n order,\n type\n );\n });\n });\n}\n\nexport default function initSort() {\n selectAll(\"table.sortable\").forEach(el => {\n sortableTable(el as HTMLTableElement);\n });\n selectAll(\"ol.journal\").forEach(el => {\n sortableJournal(el as HTMLOListElement);\n });\n}\n\ne.on(\"page-loaded\", () => {\n initSort();\n});\n","// Account trees.\n//\n// This handles the toggling of accounts in the accounts trees.\n\nimport { select, selectAll, delegate } from \"./helpers\";\nimport e from \"./events\";\n\ne.on(\"page-loaded\", () => {\n selectAll(\".tree-table\").forEach(table => {\n const expandAllLink = select(\".expand-all\", table);\n if (!expandAllLink) {\n return;\n }\n\n expandAllLink.addEventListener(\"click\", () => {\n expandAllLink.classList.add(\"hidden\");\n selectAll(\".toggled\", table).forEach(el => {\n el.classList.remove(\"toggled\");\n });\n });\n\n delegate(table, \"click\", \"span.has-children\", (event: MouseEvent) => {\n if (!event.target) {\n return;\n }\n const target = event.target as HTMLElement;\n if (target.tagName === \"A\") {\n return;\n }\n const row = target.closest(\"li\")!;\n const willShow = row.classList.contains(\"toggled\");\n if (event.shiftKey) {\n selectAll(\"li\", row).forEach(el => {\n el.classList.toggle(\"toggled\", !willShow);\n });\n }\n if (event.ctrlKey || event.metaKey) {\n selectAll(\"li\", row).forEach(el => {\n el.classList.toggle(\"toggled\", willShow);\n });\n }\n row.classList.toggle(\"toggled\");\n\n expandAllLink.classList.toggle(\n \"hidden\",\n !selectAll(\".toggled\", table).length\n );\n });\n });\n});\n","import { fetchAPI } from \"./helpers\";\nimport { notify } from \"./notifications\";\n\n/**\n * Move a file, either in an import directory or a document.\n * @returns whether the file was moved successfully.\n */\nexport async function moveDocument(\n filename: string,\n account: string,\n newName: string\n): Promise {\n try {\n const msg = await fetchAPI(\"move\", {\n filename,\n account,\n newName,\n });\n notify(msg as string);\n return true;\n } catch (error) {\n notify(error, \"error\");\n return false;\n }\n}\n","\n\n\n\n\n {\n hidden = true;\n }}\n on:focusin={() => {\n hidden = false;\n }}\n on:input={() => {\n hidden = false;\n }}\n on:keydown={keydown}\n {placeholder}\n {...inputOptions} />\n
    \n {#each filteredSuggestions as { innerHTML, suggestion }, i}\n mousedown(ev, suggestion)}>\n {@html innerHTML}\n \n {/each}\n
\n
\n","\n\n\n","\n\n\n\n{#each Object.entries(data) as [directory, items]}\n

Directory: {directory}

\n {#each items as item}\n
\n {#if item.importers.length}\n {#each item.importers as info}\n

\n \n \n move(item.name, info.account, info.newName)}>\n {'Move'}\n \n \n {_('Extract')} ( {info.importer_name} )\n \n

\n {/each}\n {:else}\n

\n \n \n move(item.name, item.account, item.newName)}>\n {'Move'}\n \n

\n {/if}\n {/each}\n{/each}\n","\n\n\n\n","\n\n
\n \n \n
\n      \n        {item.basename}\n      \n