Compare commits
260 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4225f7f4ad | ||
|
|
a63f64051b | ||
|
|
bf21647c4b | ||
|
|
818e5f2d62 | ||
|
|
c65c6b94e5 | ||
|
|
0ca1f6b241 | ||
|
|
48de4e4a3c | ||
|
|
b139c60a60 | ||
|
|
05f23436ac | ||
|
|
7012193220 | ||
|
|
7f3defff7c | ||
|
|
969cce532d | ||
|
|
c2804c4917 | ||
|
|
56beb35c36 | ||
|
|
98bf17aa58 | ||
|
|
716f05d953 | ||
|
|
fb28aa9d61 | ||
|
|
efe447fb5f | ||
|
|
9656a2cf33 | ||
|
|
9b63ad1837 | ||
|
|
765f22126e | ||
|
|
d4a8cc963e | ||
|
|
b7f5a725cd | ||
|
|
5bf5e093bb | ||
|
|
719fe02df5 | ||
|
|
f18bcc708a | ||
|
|
95c2b86bcd | ||
|
|
b03a2e6fb3 | ||
|
|
1c39a9ce47 | ||
|
|
8c8b2bb35c | ||
|
|
a2e773124f | ||
|
|
2f8ec9027b | ||
|
|
ab9a41df6a | ||
|
|
2b1e7d9907 | ||
|
|
d0360218fd | ||
|
|
e9fd3072a9 | ||
|
|
1aedfb0e2e | ||
|
|
e6ae9d0288 | ||
|
|
62ce443f8b | ||
|
|
2b8bf9cc44 | ||
|
|
42d859534a | ||
|
|
f959951185 | ||
|
|
e1c8704444 | ||
|
|
8a4cfa5e3c | ||
|
|
51d105afad | ||
|
|
15192f19e0 | ||
|
|
5712e57869 | ||
|
|
9571d06df5 | ||
|
|
3b25e114e9 | ||
|
|
f7ae3fd591 | ||
|
|
ce02d2da3c | ||
|
|
bd346a0fc1 | ||
|
|
ed8243e17e | ||
|
|
02c99a7b64 | ||
|
|
96514793e4 | ||
|
|
ec8cc3a0ba | ||
|
|
e3c8ab0ca2 | ||
|
|
fefb32b04d | ||
|
|
b5a7c032ab | ||
|
|
6af24f5f44 | ||
|
|
b4a197f87f | ||
|
|
15b6fee933 | ||
|
|
9e03576c94 | ||
|
|
bfa48c5a2c | ||
|
|
890fd9c7fb | ||
|
|
42a9651950 | ||
|
|
ff01910f6a | ||
|
|
9cdb04edcb | ||
|
|
f546d4633b | ||
|
|
db07028aa0 | ||
|
|
d44ab40729 | ||
|
|
0d409f54f5 | ||
|
|
708df1c07a | ||
|
|
10186a4202 | ||
|
|
3cd2dceace | ||
|
|
376c3deab3 | ||
|
|
45d3588bb6 | ||
|
|
15e8d77a49 | ||
|
|
4d84f262ae | ||
|
|
db64ce632c | ||
|
|
956d7501ec | ||
|
|
a758a9df7a | ||
|
|
5eea25882c | ||
|
|
e954cd9fae | ||
|
|
03d79be02c | ||
|
|
7f17d2ce57 | ||
|
|
30fba11f36 | ||
|
|
d474a6a689 | ||
|
|
83d398d9d5 | ||
|
|
3929742d9c | ||
|
|
70abd78e55 | ||
|
|
dbea74b626 | ||
|
|
c6102aeb14 | ||
|
|
10c9c1ae5d | ||
|
|
9e600c6e13 | ||
|
|
9b8a8afa97 | ||
|
|
39ac97a7e0 | ||
|
|
a5cb5a709f | ||
|
|
02064c76ed | ||
|
|
d713732807 | ||
|
|
b92391d177 | ||
|
|
4964ae261f | ||
|
|
ad847299c4 | ||
|
|
7ce099c3d0 | ||
|
|
9c7db48362 | ||
|
|
88f15cba35 | ||
|
|
d852042ccb | ||
|
|
5bd2e8a423 | ||
|
|
c27256c00a | ||
|
|
010206298e | ||
|
|
678ec31229 | ||
|
|
23d52703fd | ||
|
|
45b70c952f | ||
|
|
9f2c353e86 | ||
|
|
cfe142b285 | ||
|
|
19a25fd895 | ||
|
|
8dcb97c05a | ||
|
|
b49af327cf | ||
|
|
38ab3a49c1 | ||
|
|
9f901e34ae | ||
|
|
f050a6b0de | ||
|
|
e33f3e1c85 | ||
|
|
02ff95acde | ||
|
|
2436239964 | ||
|
|
18a0030354 | ||
|
|
ee726eece2 | ||
|
|
1cd9ba69d1 | ||
|
|
30a1cd399a | ||
|
|
0524e7f3e9 | ||
|
|
14cd912df7 | ||
|
|
f087311f7c | ||
|
|
61d1fc2678 | ||
|
|
6b2566c01f | ||
|
|
31ea3bce41 | ||
|
|
2d60ea6c8d | ||
|
|
56994a522d | ||
|
|
aa90a9d052 | ||
|
|
32e0aa8ca3 | ||
|
|
e052b005ec | ||
|
|
2f18ac90f9 | ||
|
|
612f93c129 | ||
|
|
c0826c9a31 | ||
|
|
7e2de8f588 | ||
|
|
5aaa7a6634 | ||
|
|
f7feecc751 | ||
|
|
cfe1624730 | ||
|
|
95d7eb977f | ||
|
|
794380fc03 | ||
|
|
5a05620257 | ||
|
|
bebe884615 | ||
|
|
10c03880c7 | ||
|
|
3fcdfc8f83 | ||
|
|
914553ddf5 | ||
|
|
b4fe0b954c | ||
|
|
7557ee6794 | ||
|
|
382c5e7da9 | ||
|
|
6286dadc27 | ||
|
|
81f8e34846 | ||
|
|
79717926a5 | ||
|
|
5084428c9d | ||
|
|
82295aa4aa | ||
|
|
70488fc14a | ||
|
|
4901d68b01 | ||
|
|
d671a849bb | ||
|
|
ffbdf0f563 | ||
|
|
b08fe477b6 | ||
|
|
903ed85f00 | ||
|
|
5710314252 | ||
|
|
277462d822 | ||
|
|
26691d49cd | ||
|
|
bd37db9dc4 | ||
|
|
091c4fa70c | ||
|
|
d5987410d1 | ||
|
|
6d8249160d | ||
|
|
1e046d57f4 | ||
|
|
9e3546b416 | ||
|
|
26034475f2 | ||
|
|
356e6c8268 | ||
|
|
5870fce178 | ||
|
|
855c7ab377 | ||
|
|
ccd8ba66b2 | ||
|
|
093763909f | ||
|
|
115e874a39 | ||
|
|
bd2b65d068 | ||
|
|
b7c39113e7 | ||
|
|
05288daf17 | ||
|
|
79421df5cd | ||
|
|
8d5d37fa0b | ||
|
|
1029b9131b | ||
|
|
3bea63dc8a | ||
|
|
856eb52a95 | ||
|
|
50017f13a7 | ||
|
|
561889738f | ||
|
|
b97e58c1f2 | ||
|
|
0f923475c8 | ||
|
|
43df69ab24 | ||
|
|
3e4cd1ecfa | ||
|
|
cfa5984d3e | ||
|
|
c9c0228a62 | ||
|
|
d87e7fc347 | ||
|
|
80eb747816 | ||
|
|
913c4130b6 | ||
|
|
ffed47e157 | ||
|
|
c7b0b4bf2b | ||
|
|
defbaefb44 | ||
|
|
18414d733b | ||
|
|
83523d6f59 | ||
|
|
97fc95f769 | ||
|
|
484575eb66 | ||
|
|
e34c10d15b | ||
|
|
025a7bebb6 | ||
|
|
bada1ae429 | ||
|
|
b7a67a0486 | ||
|
|
addfb343b0 | ||
|
|
11cb1daadf | ||
|
|
ddeaa2b958 | ||
|
|
9c1d859cfd | ||
|
|
d927012d75 | ||
|
|
72e8e277af | ||
|
|
75bac4e961 | ||
|
|
f84087156a | ||
|
|
56be56de86 | ||
|
|
f6e37be73c | ||
|
|
71bd15a4d6 | ||
|
|
7e8a8e280e | ||
|
|
672fdb59e6 | ||
|
|
b5a6fbe839 | ||
|
|
824f73d9aa | ||
|
|
d8d2fb7a85 | ||
|
|
8a922a2a5c | ||
|
|
ba56f81d19 | ||
|
|
002dea199e | ||
|
|
bb99b48833 | ||
|
|
210dfb48fc | ||
|
|
7e83d45d6c | ||
|
|
6d268ba00b | ||
|
|
e92c102bc9 | ||
|
|
20a302c3b4 | ||
|
|
bdbc570c91 | ||
|
|
17b629742d | ||
|
|
54a18b8cf2 | ||
|
|
a48efe822a | ||
|
|
45ce65c058 | ||
|
|
89fbe2e6b5 | ||
|
|
e5feff5884 | ||
|
|
93008a8e53 | ||
|
|
8bb829e1e4 | ||
|
|
70478ffcc2 | ||
|
|
6f6b1cb47d | ||
|
|
1834443246 | ||
|
|
c3c2714ace | ||
|
|
884451669c | ||
|
|
0bdc3f7e59 | ||
|
|
43b069905d | ||
|
|
d85ce273af | ||
|
|
69d137c2d7 | ||
|
|
7988e5f472 | ||
|
|
1c8465cdd9 | ||
|
|
f59705a7e7 | ||
|
|
d886f5a606 |
41
.github/workflows/tests.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
name: Tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.rst'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.rst'
|
||||
jobs:
|
||||
tests:
|
||||
name: ${{ matrix.name }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {name: Linux, python: '3.12', os: ubuntu-latest, tox: py312}
|
||||
- {name: Windows, python: '3.12', os: windows-latest, tox: py312}
|
||||
- {name: Mac, python: '3.12', os: macos-latest, tox: py312}
|
||||
- {name: Minimal, python: '3.12', os: ubuntu-latest, tox: minimal}
|
||||
- {name: '3.11', python: '3.11', os: ubuntu-latest, tox: py311}
|
||||
- {name: '3.10', python: '3.10', os: ubuntu-latest, tox: py310}
|
||||
- {name: '3.9', python: '3.9', os: ubuntu-latest, tox: py39}
|
||||
- {name: '3.8', python: '3.8', os: ubuntu-latest, tox: py38}
|
||||
- {name: '3.7', python: '3.7', os: ubuntu-latest, tox: py37}
|
||||
- {name: Style, python: '3.10', os: ubuntu-latest, tox: stylecheck}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
- name: update pip
|
||||
run: |
|
||||
pip install -U setuptools wheel
|
||||
python -m pip install -U pip
|
||||
- run: pip install tox
|
||||
- run: tox -e ${{ matrix.tox }}
|
||||
160
.gitignore
vendored
@@ -1,4 +1,160 @@
|
||||
*.egg-info
|
||||
*.pyc
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
37
.readthedocs.yaml
Normal file
@@ -0,0 +1,37 @@
|
||||
# Read the Docs configuration file for Sphinx projects
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Set the OS, Python version and other tools you might need
|
||||
build:
|
||||
os: ubuntu-lts-latest
|
||||
tools:
|
||||
python: "latest"
|
||||
# You can also specify other tool versions:
|
||||
# nodejs: "20"
|
||||
# rust: "1.70"
|
||||
# golang: "1.20"
|
||||
|
||||
# Build documentation in the "docs/" directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
# You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
|
||||
# builder: "dirhtml"
|
||||
# Fail on all warnings to avoid broken references
|
||||
fail_on_warning: true
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
# formats:
|
||||
# - pdf
|
||||
# - epub
|
||||
|
||||
# Optional but recommended, declare the Python requirements required
|
||||
# to build your documentation
|
||||
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
python:
|
||||
install:
|
||||
- path: .
|
||||
extra_requirements:
|
||||
- docs
|
||||
6
LICENSE
@@ -4,10 +4,10 @@ All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
recursive-include flask_debugtoolbar/templates/ *.html
|
||||
recursive-include flask_debugtoolbar/static/ *
|
||||
include LICENSE
|
||||
recursive-include src/flask_debugtoolbar/templates *.html
|
||||
recursive-include src/flask_debugtoolbar/static *
|
||||
|
||||
12
README
@@ -1,12 +0,0 @@
|
||||
Flask Debug-toolbar
|
||||
|
||||
A port of the django-debug toolbar (github.com/robhudson/django-debug-toolbar)
|
||||
to Flask.
|
||||
|
||||
Usage::
|
||||
|
||||
from flask import Flask
|
||||
from flask_debugtoolbar import DebugToolbarExtension
|
||||
|
||||
app = Flask(__name__)
|
||||
toolbar = DebugToolbarExtension(app)
|
||||
43
README.rst
Normal file
@@ -0,0 +1,43 @@
|
||||
Flask Debug-toolbar
|
||||
===================
|
||||
|
||||
This is a port of the excellent `django-debug-toolbar <https://github.com/jazzband/django-debug-toolbar>`_
|
||||
for Flask applications.
|
||||
|
||||
.. image:: https://github.com/pallets-eco/flask-debugtoolbar/actions/workflows/tests.yml/badge.svg
|
||||
:target: https://github.com/pallets-eco/flask-debugtoolbar/actions
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Installing is simple with pip::
|
||||
|
||||
$ pip install flask-debugtoolbar
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Setting up the debug toolbar is simple::
|
||||
|
||||
from flask import Flask
|
||||
from flask_debugtoolbar import DebugToolbarExtension
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# the toolbar is only enabled in debug mode:
|
||||
app.debug = True
|
||||
|
||||
# set a 'SECRET_KEY' to enable the Flask session cookies
|
||||
app.config['SECRET_KEY'] = '<replace with a secret key>'
|
||||
|
||||
toolbar = DebugToolbarExtension(app)
|
||||
|
||||
|
||||
The toolbar will automatically be injected into Jinja templates when debug mode is on.
|
||||
In production, setting ``app.debug = False`` will disable the toolbar.
|
||||
|
||||
See the `documentation`_ for more information.
|
||||
|
||||
.. _documentation: https://flask-debugtoolbar.readthedocs.io/
|
||||
157
docs/Makefile
Normal file
@@ -0,0 +1,157 @@
|
||||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = _build
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
html: _themes
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Flask-DebugToolbar.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Flask-DebugToolbar.qhc"
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/Flask-DebugToolbar"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Flask-DebugToolbar"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
|
||||
_themes:
|
||||
git clone git://github.com/Pylons/pylons_sphinx_theme.git _themes
|
||||
cd ..; git submodule update --init; cd docs
|
||||
0
docs/_static/.gitignore
vendored
Normal file
BIN
docs/_static/example.gif
vendored
Normal file
|
After Width: | Height: | Size: 156 KiB |
BIN
docs/_static/screenshot-config-panel.png
vendored
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
docs/_static/screenshot-headers-panel.png
vendored
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
docs/_static/screenshot-logger-panel.png
vendored
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
docs/_static/screenshot-profiler-panel.png
vendored
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
docs/_static/screenshot-request-vars-panel.png
vendored
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
docs/_static/screenshot-sqlalchemy-panel.png
vendored
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
docs/_static/screenshot-template-panel.png
vendored
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
docs/_static/screenshot-time-panel.png
vendored
Normal file
|
After Width: | Height: | Size: 42 KiB |
0
docs/_templates/.gitignore
vendored
Normal file
1
docs/_themes
Submodule
260
docs/conf.py
Normal file
@@ -0,0 +1,260 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Flask-DebugToolbar documentation build configuration file, created by
|
||||
# sphinx-quickstart on Wed Feb 15 18:08:39 2012.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import pkg_resources
|
||||
import sys
|
||||
import time
|
||||
|
||||
import flask_debugtoolbar
|
||||
|
||||
|
||||
BUILD_DATE = datetime.datetime.utcfromtimestamp(int(os.environ.get('SOURCE_DATE_EPOCH', time.time())))
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = [
|
||||
'sphinx.ext.viewcode',
|
||||
'sphinx.ext.intersphinx',
|
||||
'pallets_sphinx_themes',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Flask-DebugToolbar'
|
||||
copyright = u'2012-{0}'.format(BUILD_DATE.year)
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = flask_debugtoolbar.__version__
|
||||
# The short X.Y version.
|
||||
version = '.'.join(release.split('.')[:2])
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
intersphinx_mapping = {
|
||||
'flasksqlalchemy': ('https://flask-sqlalchemy.palletsprojects.com/', None)
|
||||
}
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'flask'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
sys.path.append(os.path.abspath('_themes'))
|
||||
html_theme_path = ['_themes']
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'Flask-DebugToolbardoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'Flask-DebugToolbar.tex', u'Flask-DebugToolbar Documentation',
|
||||
u'Matt Good', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output --------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'flask-debugtoolbar', u'Flask-DebugToolbar Documentation',
|
||||
[u'Matt Good'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output ------------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'Flask-DebugToolbar', u'Flask-DebugToolbar Documentation',
|
||||
u'Matt Good', 'Flask-DebugToolbar', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#texinfo_show_urls = 'footnote'
|
||||
98
docs/index.rst
Normal file
@@ -0,0 +1,98 @@
|
||||
Flask-DebugToolbar
|
||||
==================
|
||||
|
||||
This extension adds a toolbar overlay to Flask applications containing useful information for debugging.
|
||||
|
||||
.. image:: _static/example.gif
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Installing is simple with `pip`_::
|
||||
|
||||
$ pip install flask-debugtoolbar
|
||||
|
||||
.. _pip: https://pip.pypa.io/
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Setting up the debug toolbar is simple::
|
||||
|
||||
from flask import Flask
|
||||
from flask_debugtoolbar import DebugToolbarExtension
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# the toolbar is only enabled in debug mode:
|
||||
app.debug = True
|
||||
|
||||
# set a 'SECRET_KEY' to enable the Flask session cookies
|
||||
app.config['SECRET_KEY'] = '<replace with a secret key>'
|
||||
|
||||
toolbar = DebugToolbarExtension(app)
|
||||
|
||||
|
||||
The toolbar will automatically be injected into HTML responses when debug mode
|
||||
is on. In production, setting ``app.debug = False`` will disable the toolbar.
|
||||
|
||||
This extension also supports the Flask app factory pattern by separately
|
||||
creating the toolbar and later initializing it for an app::
|
||||
|
||||
toolbar = DebugToolbarExtension()
|
||||
# Then later on.
|
||||
app = create_app('the-config.cfg')
|
||||
toolbar.init_app(app)
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
The toolbar support several configuration options:
|
||||
|
||||
==================================== ===================================== ==========================
|
||||
Name Description Default
|
||||
==================================== ===================================== ==========================
|
||||
``DEBUG_TB_ENABLED`` Enable the toolbar? ``app.debug``
|
||||
``DEBUG_TB_HOSTS`` Whitelist of hosts to display toolbar any host
|
||||
``DEBUG_TB_INTERCEPT_REDIRECTS`` Should intercept redirects? ``True``
|
||||
``DEBUG_TB_PANELS`` List of module/class names of panels enable all built-in panels
|
||||
``DEBUG_TB_PROFILER_ENABLED`` Enable the profiler on all requests ``False``, user-enabled
|
||||
``DEBUG_TB_TEMPLATE_EDITOR_ENABLED`` Enable the template editor ``False``
|
||||
``DEBUG_TB_PROFILER_DUMP_FILENAME`` Filename of the profiler stats dump, ``None``, no dump will be written
|
||||
can be a ``str`` or a ``callable``
|
||||
==================================== ===================================== ==========================
|
||||
|
||||
To change one of the config options, set it in the Flask app's config like::
|
||||
|
||||
app.config['DEBUG_TB_INTERCEPT_REDIRECTS'] = False
|
||||
|
||||
|
||||
Panels
|
||||
------
|
||||
|
||||
.. toctree::
|
||||
|
||||
panels
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
Fork us `on GitHub <https://github.com/pallets-eco/flask-debugtoolbar>`_
|
||||
|
||||
Thanks
|
||||
------
|
||||
|
||||
This was based on the original `django-debug-toolbar`_. Thanks to `Michael van Tellingen`_ for the original development of this Flask extension, and to all the `individual contributors`_.
|
||||
|
||||
.. _django-debug-toolbar: https://github.com/jazzband/django-debug-toolbar
|
||||
.. _Michael van Tellingen: https://github.com/mvantellingen
|
||||
.. _individual contributors: https://github.com/pallets-eco/flask-debugtoolbar/graphs/contributors
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
||||
110
docs/panels.rst
Normal file
@@ -0,0 +1,110 @@
|
||||
Built-In Panels
|
||||
===============
|
||||
|
||||
Versions
|
||||
--------
|
||||
flask_debugtoolbar.panels.versions.VersionDebugPanel
|
||||
|
||||
Shows the installed Flask version. The expanded view displays all installed packages and their versions as detected by ``setuptools``.
|
||||
|
||||
|
||||
Time
|
||||
----
|
||||
|
||||
flask_debugtoolbar.panels.timer.TimerDebugPanel
|
||||
|
||||
Shows the time taken to process the current request. The expanded view includes the breakdown of CPU time, by user and system, wall clock time, and context switches.
|
||||
|
||||
.. image:: _static/screenshot-time-panel.png
|
||||
|
||||
|
||||
HTTP Headers
|
||||
------------
|
||||
|
||||
flask_debugtoolbar.panels.headers.HeaderDebugPanel
|
||||
|
||||
Displays the HTTP headers for the current request.
|
||||
|
||||
.. image:: _static/screenshot-headers-panel.png
|
||||
|
||||
|
||||
Request Vars
|
||||
------------
|
||||
|
||||
flask_debugtoolbar.panels.request_vars.RequestVarsDebugPanel
|
||||
|
||||
Displays details of the Flask request-related variables, including the view function parameters, cookies, session variables, and GET and POST variables.
|
||||
|
||||
.. image:: _static/screenshot-request-vars-panel.png
|
||||
|
||||
|
||||
Config
|
||||
------
|
||||
|
||||
flask_debugtoolbar.panels.config_vars.ConfigVarsDebugPanel
|
||||
|
||||
Shows the contents of the Flask application's config dict ``app.config``.
|
||||
|
||||
.. image:: _static/screenshot-config-panel.png
|
||||
|
||||
|
||||
Templates
|
||||
---------
|
||||
|
||||
flask_debugtoolbar.panels.template.TemplateDebugPanel
|
||||
|
||||
Shows information about the templates rendered for this request, and the value of the template parameters provided.
|
||||
|
||||
.. image:: _static/screenshot-template-panel.png
|
||||
|
||||
|
||||
SQLAlchemy
|
||||
----------
|
||||
|
||||
flask_debugtoolbar.panels.sqlalchemy.SQLAlchemyDebugPanel
|
||||
|
||||
Shows SQL queries run during the current request.
|
||||
|
||||
.. note:: This panel requires using the `Flask-SQLAlchemy`_ extension in order
|
||||
to record the queries. See the Flask-SQLAlchemy
|
||||
:ref:`flasksqlalchemy:quickstart` section to configure it.
|
||||
|
||||
For additional details on query recording see the
|
||||
:py:func:`~flask_sqlalchemy.get_debug_queries` documentation.
|
||||
|
||||
.. note:: SQL syntax highlighting requires `Pygments`_ to be installed.
|
||||
|
||||
.. image:: _static/screenshot-sqlalchemy-panel.png
|
||||
|
||||
.. _Flask-SQLAlchemy: https://flask-sqlalchemy.palletsprojects.com/
|
||||
|
||||
.. _Pygments: https://pygments.org/
|
||||
|
||||
|
||||
Logging
|
||||
-------
|
||||
|
||||
flask_debugtoolbar.panels.logger.LoggingPanel
|
||||
|
||||
Displays log messages recorded during the current request.
|
||||
|
||||
.. image:: _static/screenshot-logger-panel.png
|
||||
|
||||
|
||||
Route List
|
||||
----------
|
||||
|
||||
flask_debugtoolbar.panels.route_list.RouteListDebugPanel
|
||||
|
||||
|
||||
Displays the Flask URL routing rules.
|
||||
|
||||
|
||||
Profiler
|
||||
--------
|
||||
|
||||
flask_debugtoolbar.panels.profiler.ProfilerDebugPanel
|
||||
|
||||
Reports profiling data for the current request. Due to the performance overhead, profiling is disabled by default. Click the checkmark to toggle profiling on or off. After enabling the profiler, refresh the page to re-run it with profiling.
|
||||
|
||||
.. image:: _static/screenshot-profiler-panel.png
|
||||
@@ -1,9 +1,7 @@
|
||||
import sys
|
||||
sys.path.insert(0, '.')
|
||||
# Run using: `FLASK_DEBUG=True flask run`
|
||||
|
||||
from flask import Flask, render_template, redirect, url_for
|
||||
from flaskext.script import Manager
|
||||
from flaskext.sqlalchemy import SQLAlchemy
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_debugtoolbar import DebugToolbarExtension
|
||||
|
||||
|
||||
@@ -14,33 +12,35 @@ app.config['DEBUG_TB_INTERCEPT_REDIRECTS'] = True
|
||||
# 'flask_debugtoolbar.panels.logger.LoggingPanel',
|
||||
# 'flask_debugtoolbar.panels.timer.TimerDebugPanel',
|
||||
#)
|
||||
#app.config['DEBUG_TB_HOSTS'] = ('127.0.0.1', '::1' )
|
||||
app.config['SECRET_KEY'] = 'asd'
|
||||
app.config['DEBUG'] = True
|
||||
app.config['SQLALCHEMY_RECORD_QUERIES'] = True
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
|
||||
db = SQLAlchemy(app)
|
||||
# This is no longer needed for Flask-SQLAlchemy 3.0+, if you're using 2.X you'll want to define this:
|
||||
# app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||
|
||||
db = SQLAlchemy(app)
|
||||
toolbar = DebugToolbarExtension(app)
|
||||
|
||||
|
||||
class ExampleModel(db.Model):
|
||||
__tablename__ = 'examples'
|
||||
value = db.Column(db.String(100), primary_key=True)
|
||||
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
app.logger.info("Hello there")
|
||||
ExampleModel.query.get(1)
|
||||
return render_template('index.html')
|
||||
|
||||
|
||||
@app.route('/redirect')
|
||||
def redirect_example():
|
||||
|
||||
response = redirect(url_for('index'))
|
||||
response.set_cookie('test_cookie', '1')
|
||||
return response
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
|
||||
manager = Manager(app)
|
||||
manager.run()
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
import os
|
||||
|
||||
from flask import current_app, request, g
|
||||
from flask.globals import _request_ctx_stack
|
||||
from flask import send_from_directory
|
||||
from jinja2 import Environment, PackageLoader
|
||||
from werkzeug.exceptions import HTTPException
|
||||
from werkzeug.urls import url_quote_plus
|
||||
|
||||
from flask_debugtoolbar.toolbar import DebugToolbar
|
||||
from flask import Blueprint
|
||||
|
||||
|
||||
module = Blueprint('debugtoolbar', __name__)
|
||||
|
||||
def replace_insensitive(string, target, replacement):
|
||||
"""Similar to string.replace() but is case insensitive
|
||||
Code borrowed from: http://forums.devshed.com/python-programming-11/case-insensitive-string-replace-490921.html
|
||||
"""
|
||||
no_case = string.lower()
|
||||
index = no_case.rfind(target.lower())
|
||||
if index >= 0:
|
||||
return string[:index] + replacement + string[index + len(target):]
|
||||
else: # no results so return the original string
|
||||
return string
|
||||
|
||||
|
||||
class DebugToolbarExtension(object):
|
||||
_static_dir = os.path.realpath(
|
||||
os.path.join(os.path.dirname(__file__), 'static'))
|
||||
|
||||
_redirect_codes = [301, 302, 303, 304]
|
||||
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
self.debug_toolbars = {}
|
||||
|
||||
if not app.debug:
|
||||
return
|
||||
|
||||
if not app.config.get('SECRET_KEY'):
|
||||
raise RuntimeError(
|
||||
"The Flask-DebugToolbar requires the 'SECRET_KEY' config "
|
||||
"var to be set")
|
||||
|
||||
DebugToolbar.load_panels(app)
|
||||
|
||||
self.app.before_request(self.process_request)
|
||||
self.app.after_request(self.process_response)
|
||||
|
||||
# Monkey-patch the Flask.dispatch_request method
|
||||
app.dispatch_request = self.dispatch_request
|
||||
|
||||
# Configure jinja for the internal templates and add url rules
|
||||
# for static data
|
||||
self.jinja_env = Environment(
|
||||
autoescape=True,
|
||||
extensions=['jinja2.ext.i18n'],
|
||||
loader=PackageLoader(__name__, 'templates'))
|
||||
self.jinja_env.filters['urlencode'] = url_quote_plus
|
||||
|
||||
app.add_url_rule('/_debug_toolbar/static/<path:filename>',
|
||||
'_debug_toolbar.static', self.send_static_file)
|
||||
|
||||
app.register_blueprint(module, url_prefix='/_debug_toolbar/views')
|
||||
|
||||
def dispatch_request(self):
|
||||
"""Modified version of Flask.dispatch_request to call process_view."""
|
||||
req = _request_ctx_stack.top.request
|
||||
app = current_app
|
||||
|
||||
if req.routing_exception is not None:
|
||||
app.raise_routing_exception(req)
|
||||
|
||||
rule = req.url_rule
|
||||
|
||||
# if we provide automatic options for this URL and the
|
||||
# request came with the OPTIONS method, reply automatically
|
||||
if getattr(rule, 'provide_automatic_options', False) \
|
||||
and req.method == 'OPTIONS':
|
||||
return app.make_default_options_response()
|
||||
|
||||
# otherwise dispatch to the handler for that endpoint
|
||||
view_func = app.view_functions[rule.endpoint]
|
||||
view_func = self.process_view(app, view_func, req.view_args)
|
||||
|
||||
return view_func(**req.view_args)
|
||||
|
||||
def _show_toolbar(self):
|
||||
"""Return a boolean to indicate if we need to show the toolbar."""
|
||||
if request.path.startswith('/_debug_toolbar/'):
|
||||
return False
|
||||
return True
|
||||
|
||||
def send_static_file(self, filename):
|
||||
"""Send a static file from the flask-debugtoolbar static directory."""
|
||||
return send_from_directory(self._static_dir, filename)
|
||||
|
||||
def process_request(self):
|
||||
g.debug_toolbar = self
|
||||
|
||||
if not self._show_toolbar():
|
||||
return
|
||||
|
||||
real_request = request._get_current_object()
|
||||
|
||||
self.debug_toolbars[real_request] = DebugToolbar(real_request, self.jinja_env)
|
||||
for panel in self.debug_toolbars[real_request].panels:
|
||||
panel.process_request(real_request)
|
||||
|
||||
def process_view(self, app, view_func, view_kwargs):
|
||||
""" This method is called just before the flask view is called.
|
||||
This is done by the dispatch_request method.
|
||||
"""
|
||||
real_request = request._get_current_object()
|
||||
if real_request in self.debug_toolbars:
|
||||
for panel in self.debug_toolbars[real_request].panels:
|
||||
new_view = panel.process_view(real_request, view_func, view_kwargs)
|
||||
if new_view:
|
||||
view_func = new_view
|
||||
return view_func
|
||||
|
||||
def process_response(self, response):
|
||||
real_request = request._get_current_object()
|
||||
if real_request not in self.debug_toolbars:
|
||||
return response
|
||||
|
||||
# Intercept http redirect codes and display an html page with a
|
||||
# link to the target.
|
||||
if self.debug_toolbars[real_request].config['DEBUG_TB_INTERCEPT_REDIRECTS']:
|
||||
if response.status_code in self._redirect_codes:
|
||||
redirect_to = response.location
|
||||
redirect_code = response.status_code
|
||||
if redirect_to:
|
||||
content = self.render('redirect.html', {
|
||||
'redirect_to': redirect_to,
|
||||
'redirect_code': redirect_code
|
||||
})
|
||||
response.content_length = len(content)
|
||||
response.location = None
|
||||
response.response = [content]
|
||||
response.status_code = 200
|
||||
|
||||
# If the http response code is 200 then we process to add the
|
||||
# toolbar to the returned html response.
|
||||
if (response.status_code == 200
|
||||
and response.headers['content-type'].startswith('text/html')):
|
||||
for panel in self.debug_toolbars[real_request].panels:
|
||||
panel.process_response(real_request, response)
|
||||
|
||||
if response.is_sequence:
|
||||
response_html = response.data.decode(response.charset)
|
||||
toolbar_html = self.debug_toolbars[real_request].render_toolbar()
|
||||
|
||||
content = replace_insensitive(
|
||||
response_html, '</body>', toolbar_html + '</body>')
|
||||
content = content.encode(response.charset)
|
||||
response.response = [content]
|
||||
response.content_length = len(content)
|
||||
|
||||
return response
|
||||
|
||||
def render(self, template_name, context):
|
||||
template = self.jinja_env.get_template(template_name)
|
||||
return template.render(**context)
|
||||
@@ -1,153 +0,0 @@
|
||||
import hashlib
|
||||
|
||||
try:
|
||||
from flaskext.sqlalchemy import get_debug_queries, SQLAlchemy
|
||||
except ImportError:
|
||||
sqlalchemy_available = False
|
||||
get_debug_queries = SQLAlchemy = None
|
||||
else:
|
||||
sqlalchemy_available = True
|
||||
|
||||
from flask import request, current_app, abort, json_available, g
|
||||
from flask.helpers import json
|
||||
from flask_debugtoolbar import module
|
||||
from flask_debugtoolbar.panels import DebugPanel
|
||||
from flask_debugtoolbar.utils import format_fname, format_sql
|
||||
|
||||
|
||||
_ = lambda x: x
|
||||
|
||||
class SQLAlchemyDebugPanel(DebugPanel):
|
||||
"""
|
||||
Panel that displays the time a response took in milliseconds.
|
||||
"""
|
||||
name = 'SQLAlchemy'
|
||||
|
||||
|
||||
@property
|
||||
def has_content(self):
|
||||
if not json_available or not sqlalchemy_available:
|
||||
return True # will display an error message
|
||||
return bool(get_debug_queries())
|
||||
|
||||
def process_request(self, request):
|
||||
pass
|
||||
|
||||
def process_response(self, request, response):
|
||||
pass
|
||||
|
||||
def nav_title(self):
|
||||
return _('SQLAlchemy')
|
||||
|
||||
def nav_subtitle(self):
|
||||
if not json_available or not sqlalchemy_available:
|
||||
return 'Unavailable'
|
||||
|
||||
if get_debug_queries:
|
||||
count = len(get_debug_queries())
|
||||
return "%d %s" % (count, "query" if count == 1 else "queries")
|
||||
|
||||
def title(self):
|
||||
return _('SQLAlchemy queries')
|
||||
|
||||
def url(self):
|
||||
return ''
|
||||
|
||||
def content(self):
|
||||
if not json_available or not sqlalchemy_available:
|
||||
msg = ['Missing required libraries:', '<ul>']
|
||||
if not json_available:
|
||||
msg.append('<li>simplejson</li>')
|
||||
if not sqlalchemy_available:
|
||||
msg.append('<li>Flask-SQLAlchemy</li>')
|
||||
msg.append('</ul>')
|
||||
return '\n'.join(msg)
|
||||
|
||||
queries = get_debug_queries()
|
||||
data = []
|
||||
for query in queries:
|
||||
is_select = query.statement.strip().lower().startswith('select')
|
||||
_params = ''
|
||||
try:
|
||||
_params = json.dumps(query.parameters)
|
||||
except TypeError:
|
||||
pass # object not JSON serializable
|
||||
|
||||
|
||||
hash = hashlib.sha1(
|
||||
current_app.config['SECRET_KEY'] +
|
||||
query.statement + _params).hexdigest()
|
||||
|
||||
data.append({
|
||||
'duration': query.duration,
|
||||
'sql': format_sql(query.statement, query.parameters),
|
||||
'raw_sql': query.statement,
|
||||
'hash': hash,
|
||||
'params': _params,
|
||||
'is_select': is_select,
|
||||
'context_long': query.context,
|
||||
'context': format_fname(query.context)
|
||||
})
|
||||
return self.render('panels/sqlalchemy.html', { 'queries': data})
|
||||
|
||||
# Panel views
|
||||
|
||||
@module.route('/sqlalchemy/sql_select', methods=['GET', 'POST'])
|
||||
def sql_select():
|
||||
return ''
|
||||
statement = request.args['sql']
|
||||
params = request.args['params']
|
||||
|
||||
# Validate hash
|
||||
hash = hashlib.sha1(
|
||||
current_app.config['SECRET_KEY'] + statement + params).hexdigest()
|
||||
if hash != request.args['hash']:
|
||||
return abort(406)
|
||||
|
||||
# Make sure it is a select statement
|
||||
if not statement.lower().strip().startswith('select'):
|
||||
return abort(406)
|
||||
|
||||
params = json.loads(params)
|
||||
|
||||
engine = SQLAlchemy().get_engine(current_app)
|
||||
|
||||
result = engine.execute(statement, params)
|
||||
return g.debug_toolbar.render('panels/sqlalchemy_select.html', {
|
||||
'result': result.fetchall(),
|
||||
'headers': result.keys(),
|
||||
'sql': format_sql(statement, params),
|
||||
'duration': float(request.args['duration']),
|
||||
})
|
||||
|
||||
@module.route('/sqlalchemy/sql_explain', methods=['GET', 'POST'])
|
||||
def sql_explain():
|
||||
statement = request.args['sql']
|
||||
params = request.args['params']
|
||||
|
||||
# Validate hash
|
||||
hash = hashlib.sha1(
|
||||
current_app.config['SECRET_KEY'] + statement + params).hexdigest()
|
||||
if hash != request.args['hash']:
|
||||
return abort(406)
|
||||
|
||||
# Make sure it is a select statement
|
||||
if not statement.lower().strip().startswith('select'):
|
||||
return abort(406)
|
||||
|
||||
params = json.loads(params)
|
||||
|
||||
engine = SQLAlchemy().get_engine(current_app)
|
||||
|
||||
if engine.driver == 'pysqlite':
|
||||
query = 'EXPLAIN QUERY PLAN %s' % statement
|
||||
else:
|
||||
query = 'EXPLAIN %s' % statement
|
||||
|
||||
result = engine.execute(query, params)
|
||||
return g.debug_toolbar.render('panels/sqlalchemy_explain.html', {
|
||||
'result': result.fetchall(),
|
||||
'headers': result.keys(),
|
||||
'sql': format_sql(statement, params),
|
||||
'duration': float(request.args['duration']),
|
||||
})
|
||||
@@ -1,44 +0,0 @@
|
||||
from flask import template_rendered
|
||||
from flask_debugtoolbar.panels import DebugPanel
|
||||
|
||||
_ = lambda x: x
|
||||
|
||||
class TemplateDebugPanel(DebugPanel):
|
||||
"""
|
||||
Panel that displays the time a response took in milliseconds.
|
||||
"""
|
||||
name = 'Template'
|
||||
has_content = True
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(self.__class__, self).__init__(*args, **kwargs)
|
||||
self.templates = []
|
||||
template_rendered.connect(self._store_template_info)
|
||||
|
||||
def _store_template_info(self, sender, **kwargs):
|
||||
self.templates.append(kwargs)
|
||||
|
||||
def process_request(self, request):
|
||||
pass
|
||||
|
||||
def process_response(self, request, response):
|
||||
pass
|
||||
|
||||
def nav_title(self):
|
||||
return _('Templates')
|
||||
|
||||
def nav_subtitle(self):
|
||||
return "%d rendered" % len(self.templates)
|
||||
|
||||
def title(self):
|
||||
return _('Templates')
|
||||
|
||||
def url(self):
|
||||
return ''
|
||||
|
||||
def content(self):
|
||||
return self.render('panels/template.html', {
|
||||
'templates': self.templates
|
||||
})
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
from flask import __version__ as flask_version
|
||||
from flask_debugtoolbar.panels import DebugPanel
|
||||
|
||||
_ = lambda x: x
|
||||
|
||||
class VersionDebugPanel(DebugPanel):
|
||||
"""
|
||||
Panel that displays the Flask version.
|
||||
"""
|
||||
name = 'Version'
|
||||
has_content = False
|
||||
|
||||
def nav_title(self):
|
||||
return _('Versions')
|
||||
|
||||
def nav_subtitle(self):
|
||||
return 'Flask %s' % flask_version
|
||||
|
||||
def url(self):
|
||||
return ''
|
||||
|
||||
def title(self):
|
||||
return _('Versions')
|
||||
|
||||
def content(self):
|
||||
return None
|
||||
|
||||
|
||||
@@ -1,418 +0,0 @@
|
||||
/* Debug Toolbar CSS Reset, adapted from Eric Meyer's CSS Reset */
|
||||
#flDebug {color:#000;background:#FFF;}
|
||||
#flDebug, #flDebug div, #flDebug span, #flDebug applet, #flDebug object, #flDebug iframe,
|
||||
#flDebug h1, #flDebug h2, #flDebug h3, #flDebug h4, #flDebug h5, #flDebug h6, #flDebug p, #flDebug blockquote, #flDebug pre,
|
||||
#flDebug a, #flDebug abbr, #flDebug acronym, #flDebug address, #flDebug big, #flDebug cite, #flDebug code,
|
||||
#flDebug del, #flDebug dfn, #flDebug em, #flDebug font, #flDebug img, #flDebug ins, #flDebug kbd, #flDebug q, #flDebug s, #flDebug samp,
|
||||
#flDebug small, #flDebug strike, #flDebug strong, #flDebug sub, #flDebug sup, #flDebug tt, #flDebug var,
|
||||
#flDebug b, #flDebug u, #flDebug i, #flDebug center,
|
||||
#flDebug dl, #flDebug dt, #flDebug dd, #flDebug ol, #flDebug ul, #flDebug li,
|
||||
#flDebug fieldset, #flDebug form, #flDebug label, #flDebug legend,
|
||||
#flDebug table, #flDebug caption, #flDebug tbody, #flDebug tfoot, #flDebug thead, #flDebug tr, #flDebug th, #flDebug td {
|
||||
margin:0;
|
||||
padding:0;
|
||||
border:0;
|
||||
outline:0;
|
||||
font-size:12px;
|
||||
line-height:1.5em;
|
||||
color:#000;
|
||||
vertical-align:baseline;
|
||||
background:transparent;
|
||||
font-family:sans-serif;
|
||||
text-align:left;
|
||||
}
|
||||
|
||||
#flDebug #flDebugToolbar {
|
||||
background:#111;
|
||||
width:200px;
|
||||
z-index:100000000;
|
||||
position:fixed;
|
||||
top:0;
|
||||
bottom:0;
|
||||
right:0;
|
||||
opacity:0.9;
|
||||
}
|
||||
|
||||
#flDebug #flDebugToolbar small {
|
||||
color:#999;
|
||||
}
|
||||
|
||||
#flDebug #flDebugToolbar ul {
|
||||
margin:0;
|
||||
padding:0;
|
||||
list-style:none;
|
||||
}
|
||||
|
||||
#flDebug #flDebugToolbar li {
|
||||
border-bottom:1px solid #222;
|
||||
color:#fff;
|
||||
display:block;
|
||||
font-weight:bold;
|
||||
float:none;
|
||||
margin:0;
|
||||
padding:0;
|
||||
position:relative;
|
||||
width:auto;
|
||||
}
|
||||
|
||||
#flDebug #flDebugToolbar li>a,
|
||||
#flDebug #flDebugToolbar li>div.contentless {
|
||||
font-weight:normal;
|
||||
font-style:normal;
|
||||
text-decoration:none;
|
||||
display:block;
|
||||
font-size:16px;
|
||||
padding:10px 10px 5px 25px;
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
#flDebug #flDebugToolbar li a:hover {
|
||||
color:#111;
|
||||
background-color:#ffc;
|
||||
}
|
||||
|
||||
#flDebug #flDebugToolbar li.active {
|
||||
background-image:url(../img/indicator.png);
|
||||
background-repeat:no-repeat;
|
||||
background-position:left center;
|
||||
background-color:#333;
|
||||
padding-left:10px;
|
||||
}
|
||||
|
||||
#flDebug #flDebugToolbar li.active a:hover {
|
||||
color:#b36a60;
|
||||
background-color:transparent;
|
||||
}
|
||||
|
||||
#flDebug #flDebugToolbar li small {
|
||||
font-size:12px;
|
||||
color:#999;
|
||||
font-style:normal;
|
||||
text-decoration:none;
|
||||
font-variant:small-caps;
|
||||
}
|
||||
|
||||
#flDebug #flDebugToolbar li .switch {
|
||||
font-size: 10px;
|
||||
position: absolute;
|
||||
display: block;
|
||||
color: white;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
cursor: pointer;
|
||||
top: 15px;
|
||||
right: 2px;
|
||||
}
|
||||
|
||||
#flDebug #flDebugToolbar li .switch.active {
|
||||
background-image: url(../img/tick.png);
|
||||
}
|
||||
|
||||
#flDebug #flDebugToolbar li .switch.inactive {
|
||||
background-image: url(../img/tick-red.png);
|
||||
}
|
||||
|
||||
|
||||
#flDebug #flDebugToolbarHandle {
|
||||
position:fixed;
|
||||
background:#fff;
|
||||
border:1px solid #111;
|
||||
top:30px;
|
||||
right:0;
|
||||
z-index:100000000;
|
||||
opacity:0.75;
|
||||
}
|
||||
|
||||
#flDebug a#flShowToolBarButton {
|
||||
display:block;
|
||||
height:75px;
|
||||
width:30px;
|
||||
border-right:none;
|
||||
border-bottom:4px solid #fff;
|
||||
border-top:4px solid #fff;
|
||||
border-left:4px solid #fff;
|
||||
color:#fff;
|
||||
font-size:10px;
|
||||
font-weight:bold;
|
||||
text-decoration:none;
|
||||
text-align:center;
|
||||
text-indent:-999999px;
|
||||
background:#000 url(../img/djdt_vertical.png) no-repeat left center;
|
||||
opacity:0.5;
|
||||
}
|
||||
|
||||
#flDebug a#flShowToolBarButton:hover {
|
||||
background-color:#111;
|
||||
padding-right:6px;
|
||||
border-top-color:#FFE761;
|
||||
border-left-color:#FFE761;
|
||||
border-bottom-color:#FFE761;
|
||||
opacity:1.0;
|
||||
}
|
||||
|
||||
#flDebug code {
|
||||
display:block;
|
||||
font-family:Consolas, Monaco, "Bitstream Vera Sans Mono", "Lucida Console", monospace;
|
||||
white-space:pre;
|
||||
overflow:auto;
|
||||
}
|
||||
|
||||
#flDebug tr.flDebugOdd {
|
||||
background-color:#f5f5f5;
|
||||
}
|
||||
|
||||
#flDebug .panelContent {
|
||||
display:none;
|
||||
position:fixed;
|
||||
margin:0;
|
||||
top:0;
|
||||
right:200px;
|
||||
bottom:0;
|
||||
left:0px;
|
||||
background-color:#eee;
|
||||
color:#666;
|
||||
z-index:100000000;
|
||||
}
|
||||
|
||||
#flDebug .panelContent > div {
|
||||
border-bottom:1px solid #ddd;
|
||||
}
|
||||
|
||||
#flDebug .flDebugPanelTitle {
|
||||
position:absolute;
|
||||
background-color:#ffc;
|
||||
color:#666;
|
||||
padding-left:20px;
|
||||
top:0;
|
||||
right:0;
|
||||
left:0;
|
||||
height:50px;
|
||||
}
|
||||
|
||||
#flDebug .flDebugPanelTitle code {
|
||||
display:inline;
|
||||
font-size:inherit;
|
||||
}
|
||||
|
||||
#flDebug .flDebugPanelContent {
|
||||
position:absolute;
|
||||
top:50px;
|
||||
right:0;
|
||||
bottom:0;
|
||||
left:0;
|
||||
height:auto;
|
||||
padding:0 0 0 20px;
|
||||
}
|
||||
|
||||
#flDebug .flDebugPanelContent .scroll {
|
||||
height:100%;
|
||||
overflow:auto;
|
||||
display:block;
|
||||
padding:0 10px 0 0;
|
||||
}
|
||||
|
||||
#flDebug h3 {
|
||||
font-size:24px;
|
||||
font-weight:normal;
|
||||
line-height:50px;
|
||||
}
|
||||
|
||||
#flDebug h4 {
|
||||
font-size:20px;
|
||||
font-weight:bold;
|
||||
margin-top:0.8em;
|
||||
}
|
||||
|
||||
#flDebug .panelContent table {
|
||||
border:1px solid #ccc;
|
||||
border-collapse:collapse;
|
||||
width:100%;
|
||||
background-color:#fff;
|
||||
display:block;
|
||||
margin-top:0.8em;
|
||||
overflow: auto;
|
||||
}
|
||||
#flDebug .panelContent tbody td,
|
||||
#flDebug .panelContent tbody th {
|
||||
vertical-align:top;
|
||||
padding:2px 3px;
|
||||
}
|
||||
#flDebug .panelContent thead th {
|
||||
padding:1px 6px 1px 3px;
|
||||
text-align:left;
|
||||
font-weight:bold;
|
||||
font-size:14px;
|
||||
}
|
||||
#flDebug .panelContent tbody th {
|
||||
width:12em;
|
||||
text-align:right;
|
||||
color:#666;
|
||||
padding-right:.5em;
|
||||
}
|
||||
|
||||
#flDebug .flTemplateHideContextDiv {
|
||||
background-color:#fff;
|
||||
}
|
||||
|
||||
/*
|
||||
#flDebug .panelContent p a:hover, #flDebug .panelContent dd a:hover {
|
||||
color:#111;
|
||||
background-color:#ffc;
|
||||
}
|
||||
|
||||
#flDebug .panelContent p {
|
||||
padding:0 5px;
|
||||
}
|
||||
|
||||
#flDebug .panelContent p, #flDebug .panelContent table, #flDebug .panelContent ol, #flDebug .panelContent ul, #flDebug .panelContent dl {
|
||||
margin:5px 0 15px;
|
||||
background-color:#fff;
|
||||
}
|
||||
#flDebug .panelContent table {
|
||||
clear:both;
|
||||
border:0;
|
||||
padding:0;
|
||||
margin:0;
|
||||
border-collapse:collapse;
|
||||
border-spacing:0;
|
||||
}
|
||||
|
||||
#flDebug .panelContent table a {
|
||||
color:#000;
|
||||
padding:2px 4px;
|
||||
}
|
||||
#flDebug .panelContent table a:hover {
|
||||
background-color:#ffc;
|
||||
}
|
||||
|
||||
#flDebug .panelContent table th {
|
||||
background-color:#333;
|
||||
font-weight:bold;
|
||||
color:#fff;
|
||||
padding:3px 7px 3px;
|
||||
text-align:left;
|
||||
cursor:pointer;
|
||||
}
|
||||
#flDebug .panelContent table td {
|
||||
padding:5px 10px;
|
||||
font-size:14px;
|
||||
background:#fff;
|
||||
color:#000;
|
||||
vertical-align:top;
|
||||
border:0;
|
||||
}
|
||||
#flDebug .panelContent table tr.flDebugOdd td {
|
||||
background:#eee;
|
||||
}
|
||||
*/
|
||||
|
||||
#flDebug .panelContent .flDebugClose {
|
||||
text-indent:-9999999px;
|
||||
display:block;
|
||||
position:absolute;
|
||||
top:4px;
|
||||
right:15px;
|
||||
height:40px;
|
||||
width:40px;
|
||||
background:url(../img/close.png) no-repeat center center;
|
||||
}
|
||||
|
||||
#flDebug .panelContent .flDebugClose:hover {
|
||||
background-image:url(../img/close_hover.png);
|
||||
}
|
||||
|
||||
#flDebug .panelContent .flDebugClose.flDebugBack {
|
||||
background-image:url(../img/back.png);
|
||||
}
|
||||
|
||||
#flDebug .panelContent .flDebugClose.flDebugBack:hover {
|
||||
background-image:url(../img/back_hover.png);
|
||||
}
|
||||
|
||||
#flDebug .panelContent dt, #flDebug .panelContent dd {
|
||||
display:block;
|
||||
}
|
||||
|
||||
#flDebug .panelContent dt {
|
||||
margin-top:0.75em;
|
||||
}
|
||||
|
||||
#flDebug .panelContent dd {
|
||||
margin-left:10px;
|
||||
}
|
||||
|
||||
#flDebug a.toggleTemplate {
|
||||
padding:4px;
|
||||
background-color:#bbb;
|
||||
-moz-border-radius:3px;
|
||||
-webkit-border-radius:3px;
|
||||
}
|
||||
|
||||
#flDebug a.toggleTemplate:hover {
|
||||
padding:4px;
|
||||
background-color:#444;
|
||||
color:#ffe761;
|
||||
-moz-border-radius:3px;
|
||||
-webkit-border-radius:3px;
|
||||
}
|
||||
|
||||
|
||||
#flDebug a.flTemplateShowContext, #flDebug a.flTemplateShowContext span.toggleArrow {
|
||||
color:#999;
|
||||
}
|
||||
|
||||
#flDebug a.flTemplateShowContext:hover, #flDebug a.flTemplateShowContext:hover span.toggleArrow {
|
||||
color:#000;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
#flDebug .flDebugSqlWrap {
|
||||
position:relative;
|
||||
}
|
||||
|
||||
#flDebug .flDebugSql {
|
||||
z-index:100000002;
|
||||
}
|
||||
|
||||
#flDebug .flSQLHideStacktraceDiv tbody th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#flDebug .flSqlExplain td {
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
#flDebug span.flDebugLineChart {
|
||||
background-color:#777;
|
||||
height:3px;
|
||||
position:absolute;
|
||||
bottom:0;
|
||||
top:0;
|
||||
left:0;
|
||||
display:block;
|
||||
z-index:1000000001;
|
||||
}
|
||||
#flDebug span.flDebugLineChartWarning {
|
||||
background-color:#900;
|
||||
}
|
||||
|
||||
#flDebug .highlight { color:#000; }
|
||||
#flDebug .highlight .err { color:#000; } /* Error */
|
||||
#flDebug .highlight .g { color:#000; } /* Generic */
|
||||
#flDebug .highlight .k { color:#000; font-weight:bold } /* Keyword */
|
||||
#flDebug .highlight .o { color:#000; } /* Operator */
|
||||
#flDebug .highlight .n { color:#000; } /* Name */
|
||||
#flDebug .highlight .mi { color:#000; font-weight:bold } /* Literal.Number.Integer */
|
||||
#flDebug .highlight .l { color:#000; } /* Literal */
|
||||
#flDebug .highlight .x { color:#000; } /* Other */
|
||||
#flDebug .highlight .p { color:#000; } /* Punctuation */
|
||||
#flDebug .highlight .m { color:#000; font-weight:bold } /* Literal.Number */
|
||||
#flDebug .highlight .s { color:#333 } /* Literal.String */
|
||||
#flDebug .highlight .w { color:#888888 } /* Text.Whitespace */
|
||||
#flDebug .highlight .il { color:#000; font-weight:bold } /* Literal.Number.Integer.Long */
|
||||
#flDebug .highlight .na { color:#333 } /* Name.Attribute */
|
||||
#flDebug .highlight .nt { color:#000; font-weight:bold } /* Name.Tag */
|
||||
#flDebug .highlight .nv { color:#333 } /* Name.Variable */
|
||||
#flDebug .highlight .s2 { color:#333 } /* Literal.String.Double */
|
||||
#flDebug .highlight .cp { color:#333 } /* Comment.Preproc */
|
||||
|
||||
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 607 B |
|
Before Width: | Height: | Size: 110 B |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 634 B |
16
flask_debugtoolbar/static/js/jquery.js
vendored
@@ -1,184 +0,0 @@
|
||||
(function(window, document, version, callback) {
|
||||
var j, d;
|
||||
var loaded = false;
|
||||
if (!(j = window.jQuery) || version > j.fn.jquery || callback(j)) {
|
||||
var script = document.createElement("script");
|
||||
script.type = "text/javascript";
|
||||
script.src = DEBUG_TOOLBAR_STATIC_PATH + "js/jquery.js";
|
||||
script.onload = script.onreadystatechange = function() {
|
||||
if (!loaded && (!(d = this.readyState) || d == "loaded" || d == "complete")) {
|
||||
callback((j = window.jQuery).noConflict(1), loaded = true);
|
||||
j(script).remove();
|
||||
}
|
||||
};
|
||||
document.documentElement.childNodes[0].appendChild(script)
|
||||
}
|
||||
})(window, document, "1.3", function($, jquery_loaded) {
|
||||
$.cookie = function(name, value, options) { if (typeof value != 'undefined') { options = options || {}; if (value === null) { value = ''; options.expires = -1; } var expires = ''; if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) { var date; if (typeof options.expires == 'number') { date = new Date(); date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000)); } else { date = options.expires; } expires = '; expires=' + date.toUTCString(); } var path = options.path ? '; path=' + (options.path) : ''; var domain = options.domain ? '; domain=' + (options.domain) : ''; var secure = options.secure ? '; secure' : ''; document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join(''); } else { var cookieValue = null; if (document.cookie && document.cookie != '') { var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { var cookie = $.trim(cookies[i]); if (cookie.substring(0, name.length + 1) == (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } };
|
||||
$('head').append('<link rel="stylesheet" href="'+DEBUG_TOOLBAR_STATIC_PATH+'css/toolbar.css?'+ Math.random() +'" type="text/css" />');
|
||||
var COOKIE_NAME = 'fldt';
|
||||
var COOKIE_NAME_ACTIVE = COOKIE_NAME +'_active';
|
||||
var fldt = {
|
||||
init: function() {
|
||||
$('#flDebug').show();
|
||||
var current = null;
|
||||
$('#flDebugPanelList li a').click(function() {
|
||||
if (!this.className) {
|
||||
return false;
|
||||
}
|
||||
current = $('#flDebug #' + this.className + '-content');
|
||||
if (current.is(':visible')) {
|
||||
$(document).trigger('close.flDebug');
|
||||
$(this).parent().removeClass('active');
|
||||
} else {
|
||||
$('.panelContent').hide(); // Hide any that are already open
|
||||
current.show();
|
||||
$('#flDebugToolbar li').removeClass('active');
|
||||
$(this).parent().addClass('active');
|
||||
}
|
||||
return false;
|
||||
});
|
||||
$('#flDebugPanelList li .switch').click(function() {
|
||||
var $panel = $(this).parent();
|
||||
var $this = $(this);
|
||||
var dom_id = $panel.attr('id');
|
||||
|
||||
// Turn cookie content into an array of active panels
|
||||
var active_str = $.cookie(COOKIE_NAME_ACTIVE);
|
||||
var active = (active_str) ? active_str.split(';') : [];
|
||||
active = $.grep(active, function(n,i) { return n != dom_id; });
|
||||
|
||||
if ($this.hasClass('active')) {
|
||||
$this.removeClass('active');
|
||||
$this.addClass('inactive');
|
||||
}
|
||||
else {
|
||||
active.push(dom_id);
|
||||
$this.removeClass('inactive');
|
||||
$this.addClass('active');
|
||||
}
|
||||
|
||||
if (active.length > 0) {
|
||||
$.cookie(COOKIE_NAME_ACTIVE, active.join(';'), {
|
||||
path: '/', expires: 10
|
||||
});
|
||||
}
|
||||
else {
|
||||
$.cookie(COOKIE_NAME_ACTIVE, null, {
|
||||
path: '/', expires: -1
|
||||
});
|
||||
}
|
||||
});
|
||||
$('#flDebug a.flDebugClose').click(function() {
|
||||
$(document).trigger('close.flDebug');
|
||||
$('#flDebugToolbar li').removeClass('active');
|
||||
return false;
|
||||
});
|
||||
$('#flDebug a.remoteCall').click(function() {
|
||||
$('#flDebugWindow').load(this.href, {}, function() {
|
||||
$('#flDebugWindow a.flDebugBack').click(function() {
|
||||
$(this).parent().parent().hide();
|
||||
return false;
|
||||
});
|
||||
});
|
||||
$('#flDebugWindow').show();
|
||||
return false;
|
||||
});
|
||||
$('#flDebugTemplatePanel a.flTemplateShowContext').click(function() {
|
||||
fldt.toggle_arrow($(this).children('.toggleArrow'))
|
||||
fldt.toggle_content($(this).parent().next());
|
||||
return false;
|
||||
});
|
||||
$('#flDebugSQLPanel a.flSQLShowStacktrace').click(function() {
|
||||
fldt.toggle_content($('.flSQLHideStacktraceDiv', $(this).parents('tr')));
|
||||
return false;
|
||||
});
|
||||
$('#flHideToolBarButton').click(function() {
|
||||
fldt.hide_toolbar(true);
|
||||
return false;
|
||||
});
|
||||
$('#flShowToolBarButton').click(function() {
|
||||
fldt.show_toolbar();
|
||||
return false;
|
||||
});
|
||||
$(document).bind('close.flDebug', function() {
|
||||
// If a sub-panel is open, close that
|
||||
if ($('#flDebugWindow').is(':visible')) {
|
||||
$('#flDebugWindow').hide();
|
||||
return;
|
||||
}
|
||||
// If a panel is open, close that
|
||||
if ($('.panelContent').is(':visible')) {
|
||||
$('.panelContent').hide();
|
||||
return;
|
||||
}
|
||||
// Otherwise, just minimize the toolbar
|
||||
if ($('#flDebugToolbar').is(':visible')) {
|
||||
fldt.hide_toolbar(true);
|
||||
return;
|
||||
}
|
||||
});
|
||||
if ($.cookie(COOKIE_NAME)) {
|
||||
fldt.hide_toolbar(false);
|
||||
} else {
|
||||
fldt.show_toolbar(false);
|
||||
}
|
||||
},
|
||||
toggle_content: function(elem) {
|
||||
if (elem.is(':visible')) {
|
||||
elem.hide();
|
||||
} else {
|
||||
elem.show();
|
||||
}
|
||||
},
|
||||
close: function() {
|
||||
$(document).trigger('close.flDebug');
|
||||
return false;
|
||||
},
|
||||
hide_toolbar: function(setCookie) {
|
||||
// close any sub panels
|
||||
$('#flDebugWindow').hide();
|
||||
// close all panels
|
||||
$('.panelContent').hide();
|
||||
$('#flDebugToolbar li').removeClass('active');
|
||||
// finally close toolbar
|
||||
$('#flDebugToolbar').hide('fast');
|
||||
$('#flDebugToolbarHandle').show();
|
||||
// Unbind keydown
|
||||
$(document).unbind('keydown.flDebug');
|
||||
if (setCookie) {
|
||||
$.cookie(COOKIE_NAME, 'hide', {
|
||||
path: '/',
|
||||
expires: 10
|
||||
});
|
||||
}
|
||||
},
|
||||
show_toolbar: function(animate) {
|
||||
// Set up keybindings
|
||||
$(document).bind('keydown.flDebug', function(e) {
|
||||
if (e.keyCode == 27) {
|
||||
fldt.close();
|
||||
}
|
||||
});
|
||||
$('#flDebugToolbarHandle').hide();
|
||||
if (animate) {
|
||||
$('#flDebugToolbar').show('fast');
|
||||
} else {
|
||||
$('#flDebugToolbar').show();
|
||||
}
|
||||
$.cookie(COOKIE_NAME, null, {
|
||||
path: '/',
|
||||
expires: -1
|
||||
});
|
||||
},
|
||||
toggle_arrow: function(elem) {
|
||||
var uarr = String.fromCharCode(0x25b6);
|
||||
var darr = String.fromCharCode(0x25bc);
|
||||
elem.html(elem.html() == uarr ? darr : uarr);
|
||||
}
|
||||
};
|
||||
$(document).ready(function() {
|
||||
fldt.init();
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,54 +0,0 @@
|
||||
<div id="flDebug" style="display:none;">
|
||||
<script type="text/javascript">var DEBUG_TOOLBAR_STATIC_PATH = '{{ static_path }}'</script>
|
||||
<script type="text/javascript" src="{{ static_path }}js/toolbar.js"></script>
|
||||
|
||||
<div style="display: none;" id="flDebugToolbar">
|
||||
<ol id="flDebugPanelList">
|
||||
{% if panels %}
|
||||
<li><a id="flHideToolBarButton" href="#" title="Hide Toolbar">Hide »</a></li>
|
||||
{% else %}
|
||||
<li id="flDebugButton">DEBUG</li>
|
||||
{% endif %}
|
||||
{% for panel in panels %}
|
||||
<li id="{{ panel.dom_id() }}">
|
||||
{% if panel.has_content %}
|
||||
<a href="{{ panel.url()|default("#") }}" title="{{ panel.title() }}" class="{{ panel.dom_id() }}">
|
||||
{% else %}
|
||||
<div class="contentless">
|
||||
{% endif %}
|
||||
|
||||
{{ panel.nav_title() }}
|
||||
{% if panel.nav_subtitle() %}<br /><small>{{ panel.nav_subtitle() }}</small>{% endif %}
|
||||
|
||||
{% if panel.has_content %}
|
||||
</a>
|
||||
{% else %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if panel.user_activate %}
|
||||
<span class="switch {{ 'active' if panel.is_active else 'inactive' }}" title="Enable or disable the panel"></span>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
</div>
|
||||
<div style="display:none;" id="flDebugToolbarHandle">
|
||||
<a title="Show Toolbar" id="flShowToolBarButton" href="#">«</a>
|
||||
</div>
|
||||
{% for panel in panels %}
|
||||
{% if panel.has_content %}
|
||||
<div id="{{ panel.dom_id() }}-content" class="panelContent">
|
||||
<div class="flDebugPanelTitle">
|
||||
<a href="" class="flDebugClose">Close</a>
|
||||
<h3>{{ panel.title()|safe }}</h3>
|
||||
</div>
|
||||
<div class="flDebugPanelContent">
|
||||
<div class="scroll">
|
||||
{{ panel.content()|safe }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<div id="flDebugWindow" class="panelContent"></div>
|
||||
</div>
|
||||
@@ -1,16 +0,0 @@
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Key</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key, value in headers.iteritems() %}
|
||||
<tr class="{{ loop.cycle('djDebugOdd' 'djDebugEven') }}">
|
||||
<td>{{ key|escape }}</td>
|
||||
<td>{{ value|escape }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -1,26 +0,0 @@
|
||||
{% if records %}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Level</th>
|
||||
<th>Time</th>
|
||||
<th>Message</th>
|
||||
<th>Location</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for record in records %}
|
||||
<tr class="{{ loop.cycle('fjDebugOdd' 'fjDebugEven') }}">
|
||||
<td>{{ record.level }}</td>
|
||||
<td>{{ record.time }}</td>
|
||||
<td>{{ record.message }}</td>
|
||||
<td title="{{ record.file_long }}:{{ record.line }}">{{ record.file }}:{{ record.line }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>No messages logged.</p>
|
||||
{% endif %}
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Calls</th>
|
||||
<th>Total Time (ms)</th>
|
||||
<th>Per Call (ms)</th>
|
||||
<th>Cumulative Time (ms)</th>
|
||||
<th>Per Call (ms)</th>
|
||||
<th>Function</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in function_calls %}
|
||||
<tr class="{{ loop.cycle('flDebugOdd' 'flDebugEven') }}">
|
||||
<td>{{ row.ncalls }}</td>
|
||||
<td>{{ row.tottime }}</td>
|
||||
<td>{{ '%.4f'|format(row.percall) }}</td>
|
||||
<td>{{ row.cumtime }}</td>
|
||||
<td>{{ '%.4f'|format(row.percall_cum) }}</td>
|
||||
<td title="{{ row.filename_long }}">{{ row.filename|escape }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
|
||||
<h4>View information</h4>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>View Function</th>
|
||||
<th>args</th>
|
||||
<th>kwargs</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ view_func }}</td>
|
||||
<td>{{ view_args|default("None") }}</td>
|
||||
<td>
|
||||
{% if view_kwargs.items() %}
|
||||
{% for k, v in view_kwargs.items() %}
|
||||
{{ k }}={{ v }}{% if not loop.last %}, {% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
None
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h4>COOKIES Variables</h4>
|
||||
{% if cookies %}
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style="width:20%"/>
|
||||
<col/>
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Variable</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key, value in cookies %}
|
||||
<tr class="{{ loop.cycle('flDebugOdd' 'flDebugEven') }}">
|
||||
<td>{{ key|escape }}</td>
|
||||
<td>{{ value|escape }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>No COOKIE data</p>
|
||||
{% endif %}
|
||||
|
||||
<h4>SESSION Variables</h4>
|
||||
{% if session %}
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style="width:20%"/>
|
||||
<col/>
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Variable</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key, value in session %}
|
||||
<tr class="{{ loop.cycle('flDebugOdd' 'flDebugEven') }}">
|
||||
<td>{{ key|escape }}</td>
|
||||
<td>{{ value|escape }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>No SESSION data</p>
|
||||
{% endif %}
|
||||
|
||||
<h4>GET Variables</h4>
|
||||
{% if get %}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Variable</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key, value in get %}
|
||||
<tr class="{{ loop.cycle('flDebugOdd' 'flDebugEven') }}">
|
||||
<td>{{ key|escape }}</td>
|
||||
<td>{{ value|join(", ")|escape }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>No GET data</p>
|
||||
{% endif %}
|
||||
|
||||
<h4>POST Variables</h4>
|
||||
{% if post %}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Variable</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key, value in post %}
|
||||
<tr class="{{ loop.cycle('row1' 'row2') }}">
|
||||
<td>{{ key|escape }}</td>
|
||||
<td>{{ value|join(", ")|escape }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>No POST data</p>
|
||||
{% endif %}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th> (ms)</th>
|
||||
<th>Action</th>
|
||||
<th>Context</th>
|
||||
<th>Query</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for query in queries %}
|
||||
<tr class="{{ loop.cycle('flDebugOdd' 'flDebugEven') }}">
|
||||
<td>{{ '%.4f'|format(query.duration) }}</td>
|
||||
<td>
|
||||
{% if query.params %}
|
||||
{% if query.is_select %}
|
||||
<a class="remoteCall" href="/_debug_toolbar/views/sqlalchemy/sql_select?sql={{ query.raw_sql|urlencode }}&params={{ query.params|urlencode }}&duration={{ query.duration|urlencode }}&hash={{ query.hash }}">SELECT</a><br />
|
||||
<a class="remoteCall" href="/_debug_toolbar/views/sqlalchemy/sql_explain?sql={{ query.raw_sql|urlencode }}&params={{ query.params|urlencode }}&duration={{ query.duration|urlencode }}&hash={{ query.hash }}">EXPLAIN</a><br />
|
||||
{% if is_mysql %}
|
||||
<a class="remoteCall" href="/__debug__/sql_profile/?sql={{ query.raw_sql|urlencode }}&params={{ query.params|urlencode }}&duration={{ query.duration|urlencode }}&hash={{ query.hash }}">PROFILE</a><br />
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td title="{{ query.context_long }}">
|
||||
{{ query.context }}
|
||||
</td>
|
||||
<td class="syntax">
|
||||
<div class="djDebugSqlWrap">
|
||||
<div class="flDebugSql">{{ query.sql|safe }}</div>
|
||||
{#
|
||||
{% if query.stacktrace %}
|
||||
<div class="djSQLHideStacktraceDiv" style="display:none;">
|
||||
<table>
|
||||
<tr>
|
||||
<th>{% trans "Line" %}</th>
|
||||
<th>{% trans "Method" %}</th>
|
||||
<th>{% trans "File" %}</th>
|
||||
</tr>
|
||||
{% for file, line, method in query.stacktrace %}
|
||||
<tr>
|
||||
<td>{{ line }}</td>
|
||||
<td><code>{{ method|escape }}</code></td>
|
||||
<td><code>{{ file|escape }}</code></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% if query.template_info %}
|
||||
<table>
|
||||
{% for line in query.template_info.context %}
|
||||
<tr>
|
||||
<td>{{ line.num }}</td>
|
||||
<td><code style="font-family: monospace;{% if line.highlight %}background-color: lightgrey{% endif %}">{{ line.content }}</code></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<p><strong>{{ query.template_info.name|default:"(unknown)" }}</strong></p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<span class="djDebugLineChart{% if query.is_slow %} djDebugLineChartWarning{% endif %}" style="width:{{ query.width_ratio }}%; left:{{ query.start_offset }}%;"></span>
|
||||
#}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -1,33 +0,0 @@
|
||||
<div class="flDebugPanelTitle">
|
||||
<a class="flDebugClose flDebugBack" href="">Back</a>
|
||||
<h3>SQL Explained</h3>
|
||||
</div>
|
||||
<div class="flDebugPanelContent">
|
||||
<div class="scroll">
|
||||
<dl>
|
||||
<dt>Executed SQL</dt>
|
||||
<dd>{{ sql|safe }}</dd>
|
||||
<dt>Time</dt>
|
||||
<dd>{{ '%.4f'|format(duration) }} ms</dd>
|
||||
</dl>
|
||||
<table class="djSqlExplain">
|
||||
<thead>
|
||||
<tr>
|
||||
{% for h in headers %}
|
||||
<th>{{ h|upper }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in result %}
|
||||
<tr class="{{ loop.cycle('fjDebugOdd' 'fjDebugEven') }}">
|
||||
{% for column in row %}
|
||||
<td>{{ column }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
<div class="flDebugPanelTitle">
|
||||
<a class="flDebugClose flDebugBack" href="">Back</a>
|
||||
<h3>SQL Explained</h3>
|
||||
</div>
|
||||
<div class="flDebugPanelContent">
|
||||
<div class="scroll">
|
||||
<dl>
|
||||
<dt>Executed SQL</dt>
|
||||
<dd>{{ sql|safe }}</dd>
|
||||
<dt>Time</dt>
|
||||
<dd>{{ '%.4f'|format(duration) }} ms</dd>
|
||||
</dl>
|
||||
{% if result %}
|
||||
<table class="flSqlSelect">
|
||||
<thead>
|
||||
<tr>
|
||||
{% for h in headers %}
|
||||
<th>{{ h|upper }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in result %}
|
||||
<tr class="{{ loop.cycle('fjDebugOdd' 'fjDebugEven') }}">
|
||||
{% for column in row %}
|
||||
<td>{{ column }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>Empty set</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
{% if templates %}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Filename</th>
|
||||
<th>Context vars</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for template in templates %}
|
||||
<tr class="{{ loop.cycle('fjDebugOdd' 'fjDebugEven') }}">
|
||||
<td>{{ template.template.name }}</td>
|
||||
<td>{{ template.context.keys()|join(', ') }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>No templates rendered.</p>
|
||||
{% endif %}
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style="width:20%"/>
|
||||
<col/>
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Resource</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key, value in rows %}
|
||||
<tr class="{{ loop.cycle('flDebugOdd', 'flDebugEven') }}">
|
||||
<td>{{ key|escape }}</td>
|
||||
<td>{{ value|escape }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Redirect intercepted</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Redirect ({{ redirect_code }})</h1>
|
||||
<p>Location: <a href="{{ redirect_to }}">{{ redirect_to }}</a></p>
|
||||
<p class="notice">
|
||||
The Flask Debug Toolbar has intercepted a redirect to the above URL for
|
||||
debug viewing purposes. You can click the above link to continue with the
|
||||
redirect as normal. If you'd like to disable this feature, you can set the
|
||||
config variable <code>DEBUG_TB_INTERCEPT_REDIRECTS</code> to <code>False</code>.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,73 +0,0 @@
|
||||
from flask import url_for, current_app
|
||||
|
||||
|
||||
|
||||
class DebugToolbar(object):
|
||||
|
||||
# default config settings
|
||||
config = {
|
||||
'DEBUG_TB_INTERCEPT_REDIRECTS': True,
|
||||
'DEBUG_TB_PANELS': (
|
||||
'flask_debugtoolbar.panels.versions.VersionDebugPanel',
|
||||
'flask_debugtoolbar.panels.timer.TimerDebugPanel',
|
||||
'flask_debugtoolbar.panels.headers.HeaderDebugPanel',
|
||||
'flask_debugtoolbar.panels.request_vars.RequestVarsDebugPanel',
|
||||
'flask_debugtoolbar.panels.template.TemplateDebugPanel',
|
||||
'flask_debugtoolbar.panels.sqlalchemy.SQLAlchemyDebugPanel',
|
||||
'flask_debugtoolbar.panels.logger.LoggingPanel',
|
||||
'flask_debugtoolbar.panels.profiler.ProfilerDebugPanel',
|
||||
)
|
||||
}
|
||||
|
||||
panel_classes = []
|
||||
|
||||
def __init__(self, request, jinja_env):
|
||||
self.jinja_env = jinja_env
|
||||
self.request = request
|
||||
self.panels = []
|
||||
|
||||
self.template_context = {
|
||||
'static_path': url_for('_debug_toolbar.static', filename='')
|
||||
}
|
||||
|
||||
self.create_panels()
|
||||
|
||||
@classmethod
|
||||
def load_panels(cls, app):
|
||||
cls.config.update(app.config)
|
||||
|
||||
for panel_path in cls.config['DEBUG_TB_PANELS']:
|
||||
dot = panel_path.rindex('.')
|
||||
panel_module, panel_classname = panel_path[:dot], panel_path[dot+1:]
|
||||
|
||||
try:
|
||||
mod = __import__(panel_module, {}, {}, [''])
|
||||
except ImportError, e:
|
||||
app.logger.warning('Disabled %s due to ImportError: %s', panel_classname, e)
|
||||
continue
|
||||
panel_class = getattr(mod, panel_classname)
|
||||
cls.panel_classes.append(panel_class)
|
||||
|
||||
def create_panels(self):
|
||||
"""
|
||||
Populate debug panels
|
||||
"""
|
||||
activated = self.request.cookies.get('fldt_active', '').split(';')
|
||||
|
||||
for panel_class in self.panel_classes:
|
||||
panel_instance = panel_class(
|
||||
context=self.template_context,
|
||||
jinja_env=self.jinja_env)
|
||||
|
||||
if panel_instance.dom_id() in activated:
|
||||
panel_instance.is_active = True
|
||||
self.panels.append(panel_instance)
|
||||
|
||||
def render_toolbar(self):
|
||||
context = self.template_context.copy()
|
||||
context.update({'panels': self.panels})
|
||||
|
||||
template = self.jinja_env.get_template('base.html')
|
||||
return template.render(**context)
|
||||
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
try:
|
||||
from pygments import highlight
|
||||
from pygments.formatters import HtmlFormatter
|
||||
from pygments.lexers import SqlLexer
|
||||
from pygments.styles import get_style_by_name
|
||||
PYGMENT_STYLE = get_style_by_name('colorful')
|
||||
HAVE_PYGMENTS = True
|
||||
except ImportError:
|
||||
HAVE_PYGMENTS = False
|
||||
|
||||
|
||||
from flask import current_app
|
||||
|
||||
def format_fname(value):
|
||||
# If the value is not an absolute path, the it is a builtin or
|
||||
# a relative file (thus a project file).
|
||||
if not os.path.isabs(value):
|
||||
if value.startswith(('{', '<')):
|
||||
return value
|
||||
if value.startswith('.' + os.path.sep):
|
||||
return value
|
||||
return '.' + os.path.sep + value
|
||||
|
||||
# If the file is absolute and within the project root handle it as
|
||||
# a project file
|
||||
if value.startswith(current_app.root_path):
|
||||
return "." + value[len(current_app.root_path):]
|
||||
|
||||
# Loop through sys.path to find the longest match and return
|
||||
# the relative path from there.
|
||||
paths = sys.path
|
||||
prefix = None
|
||||
prefix_len = 0
|
||||
for path in sys.path:
|
||||
new_prefix = os.path.commonprefix([path, value])
|
||||
if len(new_prefix) > prefix_len:
|
||||
prefix = new_prefix
|
||||
prefix_len = len(prefix)
|
||||
|
||||
if not prefix.endswith(os.path.sep):
|
||||
prefix_len -= 1
|
||||
path = value[prefix_len:]
|
||||
return '<%s>' % path
|
||||
|
||||
def format_sql(query, args):
|
||||
if not HAVE_PYGMENTS:
|
||||
return query
|
||||
|
||||
return highlight(
|
||||
query,
|
||||
SqlLexer(encoding='utf-8'),
|
||||
HtmlFormatter(encoding='utf-8', noclasses=True, style=PYGMENT_STYLE))
|
||||
|
||||
6
pyproject.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[build-system]
|
||||
requires = [
|
||||
"setuptools>=42",
|
||||
"wheel"
|
||||
]
|
||||
build-backend = "setuptools.build_meta"
|
||||
36
setup.cfg
Normal file
@@ -0,0 +1,36 @@
|
||||
[metadata]
|
||||
name = Flask-DebugToolbar
|
||||
version = 0.15.1
|
||||
author = Michael van Tellingen
|
||||
author_email = michaelvantellingen@gmail.com
|
||||
maintainer = Matt Good
|
||||
maintainer_email = matt@matt-good.net
|
||||
description = A toolbar overlay for debugging Flask applications.
|
||||
long_description = file: README.rst
|
||||
long_description_content_type = text/x-rst
|
||||
keywords = flask, debug, toolbar
|
||||
url = https://github.com/pallets-eco/flask-debugtoolbar
|
||||
project_urls =
|
||||
Changelog = https://github.com/pallets-eco/flask-debugtoolbar/releases
|
||||
Documentation = https://flask-debugtoolbar.readthedocs.io/
|
||||
classifiers =
|
||||
Development Status :: 4 - Beta
|
||||
Environment :: Web Environment
|
||||
Framework :: Flask
|
||||
Intended Audience :: Developers
|
||||
License :: OSI Approved :: BSD License
|
||||
Operating System :: OS Independent
|
||||
Programming Language :: Python
|
||||
Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
||||
Topic :: Software Development :: Libraries :: Python Modules
|
||||
|
||||
[options]
|
||||
package_dir =
|
||||
= src
|
||||
|
||||
packages = find:
|
||||
include_package_data = True
|
||||
python_requires = >=3.7
|
||||
|
||||
[options.packages.find]
|
||||
where = src
|
||||
44
setup.py
@@ -1,36 +1,20 @@
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
# Metadata goes in setup.cfg. These are here for GitHub's dependency graph.
|
||||
setup(
|
||||
name='Flask-DebugToolbar',
|
||||
version='0.6',
|
||||
url='http://github.com/mgood/flask-debugtoolbar',
|
||||
license='BSD',
|
||||
author='Michael van Tellingen',
|
||||
author_email='michaelvantellingen@gmail.com',
|
||||
maintainer='Matt Good',
|
||||
maintainer_email='matt@matt-good.net',
|
||||
description='A port of the Django debug toolbar to Flask',
|
||||
long_description=__doc__,
|
||||
zip_safe=False,
|
||||
platforms='any',
|
||||
include_package_data=True,
|
||||
packages=['flask_debugtoolbar',
|
||||
'flask_debugtoolbar.panels'
|
||||
],
|
||||
name="Flask-DebugToolbar",
|
||||
install_requires=[
|
||||
'setuptools',
|
||||
'Flask>=0.8',
|
||||
'Flask>=2.2.0',
|
||||
'Blinker',
|
||||
'itsdangerous',
|
||||
'werkzeug',
|
||||
'MarkupSafe',
|
||||
'packaging',
|
||||
],
|
||||
classifiers=[
|
||||
'Development Status :: 4 - Beta',
|
||||
'Environment :: Web Environment',
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: BSD License',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules'
|
||||
]
|
||||
extras_require={
|
||||
"docs": [
|
||||
'Sphinx>=1.2.2',
|
||||
'Pallets-Sphinx-Themes',
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
267
src/flask_debugtoolbar/__init__.py
Normal file
@@ -0,0 +1,267 @@
|
||||
import contextvars
|
||||
import os
|
||||
import urllib.parse
|
||||
import warnings
|
||||
|
||||
import flask
|
||||
from flask import Blueprint, current_app, request, g, send_from_directory, url_for
|
||||
from flask.globals import request_ctx
|
||||
|
||||
from jinja2 import __version__ as __jinja_version__
|
||||
from jinja2 import Environment, PackageLoader
|
||||
|
||||
from flask_debugtoolbar.compat import iteritems
|
||||
from flask_debugtoolbar.toolbar import DebugToolbar
|
||||
from flask_debugtoolbar.utils import decode_text, gzip_compress, gzip_decompress
|
||||
|
||||
try:
|
||||
# Python 3.8+
|
||||
from importlib.metadata import version
|
||||
|
||||
__version__ = version("Flask-DebugToolbar")
|
||||
except ImportError:
|
||||
import pkg_resources
|
||||
|
||||
__version__ = pkg_resources.get_distribution("Flask-DebugToolbar").version
|
||||
|
||||
|
||||
module = Blueprint('debugtoolbar', __name__)
|
||||
|
||||
|
||||
def replace_insensitive(string, target, replacement):
|
||||
"""Similar to string.replace() but is case insensitive
|
||||
Code borrowed from:
|
||||
http://forums.devshed.com/python-programming-11/case-insensitive-string-replace-490921.html
|
||||
"""
|
||||
no_case = string.lower()
|
||||
index = no_case.rfind(target.lower())
|
||||
if index >= 0:
|
||||
return string[:index] + replacement + string[index + len(target):]
|
||||
else: # no results so return the original string
|
||||
return string
|
||||
|
||||
|
||||
def _printable(value):
|
||||
try:
|
||||
return decode_text(repr(value))
|
||||
except Exception as e:
|
||||
return '<repr(%s) raised %s: %s>' % (
|
||||
object.__repr__(value), type(e).__name__, e)
|
||||
|
||||
|
||||
class DebugToolbarExtension(object):
|
||||
_static_dir = os.path.realpath(
|
||||
os.path.join(os.path.dirname(__file__), 'static'))
|
||||
|
||||
_toolbar_codes = [200, 201, 400, 401, 403, 404, 405, 500, 501, 502, 503, 504]
|
||||
_redirect_codes = [301, 302, 303, 304]
|
||||
|
||||
def __init__(self, app=None):
|
||||
self.app = app
|
||||
# Support threads running `flask.copy_current_request_context` without
|
||||
# poping toolbar during `teardown_request`
|
||||
self.debug_toolbars_var = contextvars.ContextVar('debug_toolbars')
|
||||
jinja_extensions = ['jinja2.ext.i18n']
|
||||
|
||||
if __jinja_version__[0] == '2':
|
||||
jinja_extensions.append('jinja2.ext.with_')
|
||||
|
||||
# Configure jinja for the internal templates and add url rules
|
||||
# for static data
|
||||
self.jinja_env = Environment(
|
||||
autoescape=True,
|
||||
extensions=jinja_extensions,
|
||||
loader=PackageLoader(__name__, 'templates'))
|
||||
self.jinja_env.filters['urlencode'] = urllib.parse.quote_plus
|
||||
self.jinja_env.filters['printable'] = _printable
|
||||
self.jinja_env.globals['url_for'] = url_for
|
||||
|
||||
if app is not None:
|
||||
self.init_app(app)
|
||||
|
||||
def init_app(self, app):
|
||||
for k, v in iteritems(self._default_config(app)):
|
||||
app.config.setdefault(k, v)
|
||||
|
||||
if not app.config['DEBUG_TB_ENABLED']:
|
||||
return
|
||||
|
||||
if not app.config.get('SECRET_KEY'):
|
||||
raise RuntimeError(
|
||||
"The Flask-DebugToolbar requires the 'SECRET_KEY' config "
|
||||
"var to be set")
|
||||
|
||||
DebugToolbar.load_panels(app)
|
||||
|
||||
app.before_request(self.process_request)
|
||||
app.after_request(self.process_response)
|
||||
app.teardown_request(self.teardown_request)
|
||||
|
||||
# Monkey-patch the Flask.dispatch_request method
|
||||
app.dispatch_request = self.dispatch_request
|
||||
|
||||
app.add_url_rule('/_debug_toolbar/static/<path:filename>',
|
||||
'_debug_toolbar.static', self.send_static_file)
|
||||
|
||||
app.register_blueprint(module, url_prefix='/_debug_toolbar/views')
|
||||
|
||||
def _default_config(self, app):
|
||||
return {
|
||||
'DEBUG_TB_ENABLED': app.debug,
|
||||
'DEBUG_TB_HOSTS': (),
|
||||
'DEBUG_TB_INTERCEPT_REDIRECTS': True,
|
||||
'DEBUG_TB_PANELS': (
|
||||
'flask_debugtoolbar.panels.versions.VersionDebugPanel',
|
||||
'flask_debugtoolbar.panels.timer.TimerDebugPanel',
|
||||
'flask_debugtoolbar.panels.headers.HeaderDebugPanel',
|
||||
'flask_debugtoolbar.panels.request_vars.RequestVarsDebugPanel',
|
||||
'flask_debugtoolbar.panels.config_vars.ConfigVarsDebugPanel',
|
||||
'flask_debugtoolbar.panels.template.TemplateDebugPanel',
|
||||
'flask_debugtoolbar.panels.sqlalchemy.SQLAlchemyDebugPanel',
|
||||
'flask_debugtoolbar.panels.logger.LoggingPanel',
|
||||
'flask_debugtoolbar.panels.route_list.RouteListDebugPanel',
|
||||
'flask_debugtoolbar.panels.profiler.ProfilerDebugPanel',
|
||||
'flask_debugtoolbar.panels.g.GDebugPanel',
|
||||
),
|
||||
'SQLALCHEMY_RECORD_QUERIES': app.debug,
|
||||
}
|
||||
|
||||
def dispatch_request(self):
|
||||
"""Modified version of Flask.dispatch_request to call process_view."""
|
||||
req = request_ctx.request
|
||||
app = current_app
|
||||
|
||||
if req.routing_exception is not None:
|
||||
app.raise_routing_exception(req)
|
||||
|
||||
rule = req.url_rule
|
||||
|
||||
# if we provide automatic options for this URL and the
|
||||
# request came with the OPTIONS method, reply automatically
|
||||
if getattr(rule, 'provide_automatic_options', False) \
|
||||
and req.method == 'OPTIONS':
|
||||
return app.make_default_options_response()
|
||||
|
||||
# otherwise dispatch to the handler for that endpoint
|
||||
view_func = app.view_functions[rule.endpoint]
|
||||
view_func = self.process_view(app, view_func, req.view_args)
|
||||
|
||||
return view_func(**req.view_args)
|
||||
|
||||
def _show_toolbar(self):
|
||||
"""Return a boolean to indicate if we need to show the toolbar."""
|
||||
if request.blueprint == 'debugtoolbar':
|
||||
return False
|
||||
|
||||
hosts = current_app.config['DEBUG_TB_HOSTS']
|
||||
if hosts and request.remote_addr not in hosts:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def send_static_file(self, filename):
|
||||
"""Send a static file from the flask-debugtoolbar static directory."""
|
||||
return send_from_directory(self._static_dir, filename)
|
||||
|
||||
def process_request(self):
|
||||
g.debug_toolbar = self
|
||||
|
||||
if not self._show_toolbar():
|
||||
return
|
||||
|
||||
real_request = request._get_current_object()
|
||||
self.debug_toolbars_var.set({})
|
||||
self.debug_toolbars_var.get()[real_request] = (
|
||||
DebugToolbar(real_request, self.jinja_env))
|
||||
|
||||
for panel in self.debug_toolbars_var.get()[real_request].panels:
|
||||
panel.process_request(real_request)
|
||||
|
||||
def process_view(self, app, view_func, view_kwargs):
|
||||
""" This method is called just before the flask view is called.
|
||||
This is done by the dispatch_request method.
|
||||
"""
|
||||
real_request = request._get_current_object()
|
||||
try:
|
||||
toolbar = self.debug_toolbars_var.get({})[real_request]
|
||||
except KeyError:
|
||||
return view_func
|
||||
|
||||
for panel in toolbar.panels:
|
||||
new_view = panel.process_view(real_request, view_func, view_kwargs)
|
||||
if new_view:
|
||||
view_func = new_view
|
||||
|
||||
return view_func
|
||||
|
||||
def process_response(self, response):
|
||||
real_request = request._get_current_object()
|
||||
if real_request not in self.debug_toolbars_var.get({}):
|
||||
return response
|
||||
|
||||
# Intercept http redirect codes and display an html page with a
|
||||
# link to the target.
|
||||
if current_app.config['DEBUG_TB_INTERCEPT_REDIRECTS']:
|
||||
if response.status_code in self._redirect_codes:
|
||||
redirect_to = response.location
|
||||
redirect_code = response.status_code
|
||||
if redirect_to:
|
||||
content = self.render('redirect.html', {
|
||||
'redirect_to': redirect_to,
|
||||
'redirect_code': redirect_code
|
||||
})
|
||||
response.content_length = len(content)
|
||||
response.location = None
|
||||
response.response = [content]
|
||||
response.status_code = 200
|
||||
|
||||
# If the http response code is an allowed code then we process to add the
|
||||
# toolbar to the returned html response.
|
||||
if not (response.status_code in self._toolbar_codes and
|
||||
response.is_sequence and
|
||||
response.headers['content-type'].startswith('text/html')):
|
||||
return response
|
||||
|
||||
content_encoding = response.headers.get('Content-Encoding')
|
||||
if content_encoding and 'gzip' in content_encoding:
|
||||
response_html = gzip_decompress(response.data).decode()
|
||||
else:
|
||||
response_html = response.get_data(as_text=True)
|
||||
|
||||
no_case = response_html.lower()
|
||||
body_end = no_case.rfind('</body>')
|
||||
|
||||
if body_end >= 0:
|
||||
before = response_html[:body_end]
|
||||
after = response_html[body_end:]
|
||||
elif no_case.startswith('<!doctype html>'):
|
||||
before = response_html
|
||||
after = ''
|
||||
else:
|
||||
warnings.warn('Could not insert debug toolbar.'
|
||||
' </body> tag not found in response.')
|
||||
return response
|
||||
|
||||
toolbar = self.debug_toolbars_var.get()[real_request]
|
||||
|
||||
for panel in toolbar.panels:
|
||||
panel.process_response(real_request, response)
|
||||
|
||||
toolbar_html = toolbar.render_toolbar()
|
||||
|
||||
content = ''.join((before, toolbar_html, after))
|
||||
content = content.encode('utf-8')
|
||||
if content_encoding and 'gzip' in content_encoding:
|
||||
content = gzip_compress(content)
|
||||
response.response = [content]
|
||||
response.content_length = len(content)
|
||||
|
||||
return response
|
||||
|
||||
def teardown_request(self, exc):
|
||||
# debug_toolbars_var won't be set under `flask.copy_current_request_context`
|
||||
self.debug_toolbars_var.get({}).pop(request._get_current_object(), None)
|
||||
|
||||
def render(self, template_name, context):
|
||||
template = self.jinja_env.get_template(template_name)
|
||||
return template.render(**context)
|
||||
9
src/flask_debugtoolbar/compat.py
Normal file
@@ -0,0 +1,9 @@
|
||||
import sys
|
||||
|
||||
PY2 = sys.version_info[0] == 2
|
||||
|
||||
|
||||
if PY2:
|
||||
iteritems = lambda d: d.iteritems()
|
||||
else:
|
||||
iteritems = lambda d: iter(d.items())
|
||||
@@ -1,16 +1,18 @@
|
||||
"""Base DebugPanel class"""
|
||||
|
||||
|
||||
class DebugPanel(object):
|
||||
"""
|
||||
Base class for debug panels.
|
||||
"""
|
||||
# name = Base
|
||||
has_content = False # If content returns something, set to true in subclass
|
||||
|
||||
# If content returns something, set to true in subclass
|
||||
has_content = False
|
||||
|
||||
# If the client is able to activate/de-activate the panel
|
||||
user_enable = False
|
||||
|
||||
|
||||
# We'll maintain a local context instance so we can expose our template
|
||||
# context variables to panels which need them:
|
||||
context = {}
|
||||
@@ -23,6 +25,29 @@ class DebugPanel(object):
|
||||
# If the client enabled the panel
|
||||
self.is_active = False
|
||||
|
||||
@classmethod
|
||||
def init_app(cls, app):
|
||||
"""Method that can be overridden by child classes.
|
||||
Can be used for setting up additional URL-rules/routes.
|
||||
|
||||
Example::
|
||||
|
||||
class UMLDiagramPanel(DebugPanel):
|
||||
|
||||
@classmethod
|
||||
def init_app(cls, app):
|
||||
app.add_url_rule(
|
||||
'/_flask_debugtoolbar_umldiagram/<path:filename>',
|
||||
'_flask_debugtoolbar_umldiagram.serve_generated_image',
|
||||
cls.serve_generated_image
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def serve_generated_image(cls, app):
|
||||
return Response(...)
|
||||
"""
|
||||
pass
|
||||
|
||||
def render(self, template_name, context):
|
||||
template = self.jinja_env.get_template(template_name)
|
||||
return template.render(**context)
|
||||
@@ -57,5 +82,3 @@ class DebugPanel(object):
|
||||
|
||||
def process_response(self, request, response):
|
||||
pass
|
||||
|
||||
|
||||
29
src/flask_debugtoolbar/panels/config_vars.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from flask import current_app
|
||||
from flask_debugtoolbar.panels import DebugPanel
|
||||
|
||||
_ = lambda x: x
|
||||
|
||||
|
||||
class ConfigVarsDebugPanel(DebugPanel):
|
||||
"""
|
||||
A panel to display all variables from Flask configuration
|
||||
"""
|
||||
name = 'ConfigVars'
|
||||
has_content = True
|
||||
|
||||
def nav_title(self):
|
||||
return _('Config')
|
||||
|
||||
def title(self):
|
||||
return _('Config')
|
||||
|
||||
def url(self):
|
||||
return ''
|
||||
|
||||
def content(self):
|
||||
context = self.context.copy()
|
||||
context.update({
|
||||
'config': current_app.config,
|
||||
})
|
||||
|
||||
return self.render('panels/config_vars.html', context)
|
||||
28
src/flask_debugtoolbar/panels/g.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from flask import g
|
||||
from flask_debugtoolbar.panels import DebugPanel
|
||||
|
||||
_ = lambda x: x
|
||||
|
||||
|
||||
class GDebugPanel(DebugPanel):
|
||||
"""
|
||||
A panel to display flask.g content.
|
||||
"""
|
||||
name = 'g'
|
||||
has_content = True
|
||||
|
||||
def nav_title(self):
|
||||
return _('flask.g')
|
||||
|
||||
def title(self):
|
||||
return _('flask.g content')
|
||||
|
||||
def url(self):
|
||||
return ''
|
||||
|
||||
def content(self):
|
||||
context = self.context.copy()
|
||||
context.update({
|
||||
'g_content': g.__dict__
|
||||
})
|
||||
return self.render('panels/g.html', context)
|
||||
@@ -12,31 +12,32 @@ from flask_debugtoolbar.utils import format_fname
|
||||
|
||||
_ = lambda x: x
|
||||
|
||||
|
||||
class ThreadTrackingHandler(logging.Handler):
|
||||
def __init__(self):
|
||||
if threading is None:
|
||||
raise NotImplementedError("threading module is not available, \
|
||||
the logging panel cannot be used without it")
|
||||
logging.Handler.__init__(self)
|
||||
self.records = {} # a dictionary that maps threads to log records
|
||||
self.records = {} # a dictionary that maps threads to log records
|
||||
|
||||
def emit(self, record):
|
||||
self.get_records().append(record)
|
||||
|
||||
def get_records(self, thread=None):
|
||||
"""
|
||||
Returns a list of records for the provided thread, of if none is provided,
|
||||
returns a list for the current thread.
|
||||
Returns a list of records for the provided thread, of if none is
|
||||
provided, returns a list for the current thread.
|
||||
"""
|
||||
if thread is None:
|
||||
thread = threading.currentThread()
|
||||
thread = threading.current_thread()
|
||||
if thread not in self.records:
|
||||
self.records[thread] = []
|
||||
return self.records[thread]
|
||||
|
||||
def clear_records(self, thread=None):
|
||||
if thread is None:
|
||||
thread = threading.currentThread()
|
||||
thread = threading.current_thread()
|
||||
if thread in self.records:
|
||||
del self.records[thread]
|
||||
|
||||
@@ -46,18 +47,27 @@ _init_lock = threading.Lock()
|
||||
|
||||
|
||||
def _init_once():
|
||||
# Initialize the logging handler once, but after werkzeug has set up its
|
||||
# default logger. Otherwise, if this sets up the logging first, werkzeug
|
||||
# will not create a default logger, so the development server's output will
|
||||
# not get printed.
|
||||
global handler
|
||||
if handler is not None:
|
||||
return
|
||||
with _init_lock:
|
||||
global handler
|
||||
if handler is not None:
|
||||
return
|
||||
handler = ThreadTrackingHandler()
|
||||
logging.root.addHandler(handler)
|
||||
return
|
||||
with _init_lock:
|
||||
if handler is not None:
|
||||
return
|
||||
|
||||
# Call werkzeug's internal logging to make sure it gets configured
|
||||
# before we add our handler. Otherwise werkzeug will see our handler
|
||||
# and not configure console logging for the request log.
|
||||
# Werkzeug's default log level is INFO so this message probably won't
|
||||
# be seen.
|
||||
try:
|
||||
from werkzeug._internal import _log
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
_log('debug', 'Initializing Flask-DebugToolbar log handler')
|
||||
|
||||
handler = ThreadTrackingHandler()
|
||||
logging.root.addHandler(handler)
|
||||
|
||||
|
||||
class LoggingPanel(DebugPanel):
|
||||
@@ -78,7 +88,8 @@ class LoggingPanel(DebugPanel):
|
||||
|
||||
def nav_subtitle(self):
|
||||
# FIXME l10n: use ngettext
|
||||
return "%s message%s" % (len(handler.get_records()), (len(handler.get_records()) == 1) and '' or 's')
|
||||
num_records = len(handler.get_records())
|
||||
return '%s message%s' % (num_records, '' if num_records == 1 else 's')
|
||||
|
||||
def title(self):
|
||||
return _('Log Messages')
|
||||
@@ -102,5 +113,3 @@ class LoggingPanel(DebugPanel):
|
||||
context.update({'records': records})
|
||||
|
||||
return self.render('panels/logger.html', context)
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import sys
|
||||
try:
|
||||
import cProfile as profile
|
||||
except ImportError:
|
||||
import profile
|
||||
import functools
|
||||
import os.path
|
||||
import pstats
|
||||
|
||||
from flask import current_app
|
||||
@@ -16,10 +14,19 @@ class ProfilerDebugPanel(DebugPanel):
|
||||
"""
|
||||
Panel that displays the time a response took with cProfile output.
|
||||
"""
|
||||
|
||||
name = 'Profiler'
|
||||
|
||||
user_activate = True
|
||||
|
||||
def __init__(self, jinja_env, context={}):
|
||||
DebugPanel.__init__(self, jinja_env, context=context)
|
||||
if current_app.config.get('DEBUG_TB_PROFILER_ENABLED'):
|
||||
self.is_active = True
|
||||
self.dump_filename = current_app.config.get(
|
||||
"DEBUG_TB_PROFILER_DUMP_FILENAME"
|
||||
)
|
||||
|
||||
def has_content(self):
|
||||
return bool(self.profiler)
|
||||
|
||||
@@ -32,7 +39,9 @@ class ProfilerDebugPanel(DebugPanel):
|
||||
|
||||
def process_view(self, request, view_func, view_kwargs):
|
||||
if self.is_active:
|
||||
return functools.partial(self.profiler.runcall, view_func)
|
||||
func = functools.partial(self.profiler.runcall, view_func)
|
||||
functools.update_wrapper(func, view_func)
|
||||
return func
|
||||
|
||||
def process_response(self, request, response):
|
||||
if not self.is_active:
|
||||
@@ -68,7 +77,7 @@ class ProfilerDebugPanel(DebugPanel):
|
||||
# Cumulative time
|
||||
current['cumtime'] = info[3] * 1000
|
||||
|
||||
# Quotient of the cumulative time divded by the number of
|
||||
# Quotient of the cumulative time divided by the number of
|
||||
# primitive calls.
|
||||
if info[0]:
|
||||
current['percall_cum'] = info[3] * 1000 / info[0]
|
||||
@@ -83,13 +92,20 @@ class ProfilerDebugPanel(DebugPanel):
|
||||
|
||||
self.stats = stats
|
||||
self.function_calls = function_calls
|
||||
# destroy the profiler just in case
|
||||
|
||||
if self.dump_filename:
|
||||
if callable(self.dump_filename):
|
||||
filename = self.dump_filename()
|
||||
else:
|
||||
filename = self.dump_filename
|
||||
self.profiler.dump_stats(filename)
|
||||
|
||||
return response
|
||||
|
||||
def title(self):
|
||||
if not self.is_active:
|
||||
return "Profiler not active"
|
||||
return 'View: %.2fms' % (float(self.stats.total_tt)*1000,)
|
||||
return 'View: %.2fms' % (float(self.stats.total_tt) * 1000,)
|
||||
|
||||
def nav_title(self):
|
||||
return 'Profiler'
|
||||
@@ -97,7 +113,7 @@ class ProfilerDebugPanel(DebugPanel):
|
||||
def nav_subtitle(self):
|
||||
if not self.is_active:
|
||||
return "in-active"
|
||||
return 'View: %.2fms' % (float(self.stats.total_tt)*1000,)
|
||||
return 'View: %.2fms' % (float(self.stats.total_tt) * 1000,)
|
||||
|
||||
def url(self):
|
||||
return ''
|
||||
@@ -111,6 +127,3 @@ class ProfilerDebugPanel(DebugPanel):
|
||||
'function_calls': self.function_calls,
|
||||
}
|
||||
return self.render('panels/profiler.html', context)
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ from flask_debugtoolbar.panels import DebugPanel
|
||||
|
||||
_ = lambda x: x
|
||||
|
||||
|
||||
class RequestVarsDebugPanel(DebugPanel):
|
||||
"""
|
||||
A panel to display request variables (POST/GET, session, cookies).
|
||||
@@ -34,14 +35,15 @@ class RequestVarsDebugPanel(DebugPanel):
|
||||
def content(self):
|
||||
context = self.context.copy()
|
||||
context.update({
|
||||
'get': [(k, self.request.args.getlist(k)) for k in self.request.args],
|
||||
'post': [(k, self.request.form.getlist(k)) for k in self.request.form],
|
||||
'cookies': [(k, self.request.cookies.get(k)) for k in self.request.cookies],
|
||||
'view_func': '%s.%s' % (self.view_func.__module__, self.view_func.__name__) if self.view_func else '[unknown]',
|
||||
'get': self.request.args.lists(),
|
||||
'post': self.request.form.lists(),
|
||||
'cookies': self.request.cookies.items(),
|
||||
'view_func': ('%s.%s' % (self.view_func.__module__,
|
||||
self.view_func.__name__)
|
||||
if self.view_func else '[unknown]'),
|
||||
'view_args': self.view_args,
|
||||
'view_kwargs': self.view_kwargs or {},
|
||||
'session': self.session.items(),
|
||||
})
|
||||
|
||||
return self.render('panels/request_vars.html', context)
|
||||
|
||||
38
src/flask_debugtoolbar/panels/route_list.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from flask_debugtoolbar.panels import DebugPanel
|
||||
from flask import current_app
|
||||
|
||||
_ = lambda x: x
|
||||
|
||||
|
||||
class RouteListDebugPanel(DebugPanel):
|
||||
"""
|
||||
Panel that displays the URL routing rules.
|
||||
"""
|
||||
name = 'RouteList'
|
||||
has_content = True
|
||||
routes = []
|
||||
|
||||
def nav_title(self):
|
||||
return _('Route List')
|
||||
|
||||
def title(self):
|
||||
return _('Route List')
|
||||
|
||||
def url(self):
|
||||
return ''
|
||||
|
||||
def nav_subtitle(self):
|
||||
count = len(self.routes)
|
||||
return '%s %s' % (count, 'route' if count == 1 else 'routes')
|
||||
|
||||
def process_request(self, request):
|
||||
self.routes = [
|
||||
rule
|
||||
for rule in current_app.url_map.iter_rules()
|
||||
if not rule.rule.startswith('/_debug_toolbar')
|
||||
]
|
||||
|
||||
def content(self):
|
||||
return self.render('panels/route_list.html', {
|
||||
'routes': self.routes,
|
||||
})
|
||||
165
src/flask_debugtoolbar/panels/sqlalchemy.py
Normal file
@@ -0,0 +1,165 @@
|
||||
try:
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
except ImportError:
|
||||
sqlalchemy_available = False
|
||||
get_recorded_queries = SQLAlchemy = None
|
||||
debug_enables_record_queries = False
|
||||
else:
|
||||
try:
|
||||
from flask_sqlalchemy.record_queries import get_recorded_queries
|
||||
debug_enables_record_queries = False
|
||||
except ImportError:
|
||||
# For flask_sqlalchemy < 3.0.0
|
||||
from flask_sqlalchemy import get_debug_queries as get_recorded_queries
|
||||
|
||||
# flask_sqlalchemy < 3.0.0 automatically enabled
|
||||
# SQLALCHEMY_RECORD_QUERIES in debug or test mode
|
||||
debug_enables_record_queries = True
|
||||
|
||||
location_property = 'context'
|
||||
else:
|
||||
location_property = 'location'
|
||||
sqlalchemy_available = True
|
||||
|
||||
from flask import request, current_app, abort, g
|
||||
from flask_debugtoolbar import module
|
||||
from flask_debugtoolbar.panels import DebugPanel
|
||||
from flask_debugtoolbar.utils import format_fname, format_sql
|
||||
import itsdangerous
|
||||
|
||||
_ = lambda x: x
|
||||
|
||||
|
||||
def query_signer():
|
||||
return itsdangerous.URLSafeSerializer(current_app.config['SECRET_KEY'],
|
||||
salt='fdt-sql-query')
|
||||
|
||||
|
||||
def is_select(statement):
|
||||
prefix = b'select' if isinstance(statement, bytes) else 'select'
|
||||
return statement.lower().strip().startswith(prefix)
|
||||
|
||||
|
||||
def dump_query(statement, params):
|
||||
if not params or not is_select(statement):
|
||||
return None
|
||||
|
||||
try:
|
||||
return query_signer().dumps([statement, params])
|
||||
except TypeError:
|
||||
return None
|
||||
|
||||
|
||||
def load_query(data):
|
||||
try:
|
||||
statement, params = query_signer().loads(request.args['query'])
|
||||
except (itsdangerous.BadSignature, TypeError):
|
||||
abort(406)
|
||||
|
||||
# Make sure it is a select statement
|
||||
if not is_select(statement):
|
||||
abort(406)
|
||||
|
||||
return statement, params
|
||||
|
||||
|
||||
def extension_used():
|
||||
return 'sqlalchemy' in current_app.extensions
|
||||
|
||||
|
||||
def recording_enabled():
|
||||
return (
|
||||
(debug_enables_record_queries and current_app.debug) or
|
||||
current_app.config.get('SQLALCHEMY_RECORD_QUERIES')
|
||||
)
|
||||
|
||||
|
||||
def is_available():
|
||||
return sqlalchemy_available and extension_used() and recording_enabled()
|
||||
|
||||
|
||||
def get_queries():
|
||||
if get_recorded_queries:
|
||||
return get_recorded_queries()
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
class SQLAlchemyDebugPanel(DebugPanel):
|
||||
"""
|
||||
Panel that displays the time a response took in milliseconds.
|
||||
"""
|
||||
name = 'SQLAlchemy'
|
||||
|
||||
@property
|
||||
def has_content(self):
|
||||
return bool(get_queries()) or not is_available()
|
||||
|
||||
def process_request(self, request):
|
||||
pass
|
||||
|
||||
def process_response(self, request, response):
|
||||
pass
|
||||
|
||||
def nav_title(self):
|
||||
return _('SQLAlchemy')
|
||||
|
||||
def nav_subtitle(self):
|
||||
count = len(get_queries())
|
||||
|
||||
if not count and not is_available():
|
||||
return 'Unavailable'
|
||||
|
||||
return '%d %s' % (count, 'query' if count == 1 else 'queries')
|
||||
|
||||
def title(self):
|
||||
return _('SQLAlchemy queries')
|
||||
|
||||
def url(self):
|
||||
return ''
|
||||
|
||||
def content(self):
|
||||
queries = get_queries()
|
||||
|
||||
if not queries and not is_available():
|
||||
return self.render('panels/sqlalchemy_error.html', {
|
||||
'sqlalchemy_available': sqlalchemy_available,
|
||||
'extension_used': extension_used(),
|
||||
'recording_enabled': recording_enabled(),
|
||||
})
|
||||
|
||||
data = []
|
||||
for query in queries:
|
||||
data.append({
|
||||
'duration': query.duration,
|
||||
'sql': format_sql(query.statement, query.parameters),
|
||||
'signed_query': dump_query(query.statement, query.parameters),
|
||||
'location_long': getattr(query, location_property),
|
||||
'location': format_fname(getattr(query, location_property))
|
||||
})
|
||||
return self.render('panels/sqlalchemy.html', {'queries': data})
|
||||
|
||||
|
||||
# Panel views
|
||||
|
||||
|
||||
@module.route('/sqlalchemy/sql_select', methods=['GET', 'POST'])
|
||||
@module.route('/sqlalchemy/sql_explain', methods=['GET', 'POST'],
|
||||
defaults=dict(explain=True))
|
||||
def sql_select(explain=False):
|
||||
statement, params = load_query(request.args['query'])
|
||||
engine = SQLAlchemy().get_engine(current_app)
|
||||
|
||||
if explain:
|
||||
if engine.driver == 'pysqlite':
|
||||
statement = 'EXPLAIN QUERY PLAN\n%s' % statement
|
||||
else:
|
||||
statement = 'EXPLAIN\n%s' % statement
|
||||
|
||||
result = engine.execute(statement, params)
|
||||
return g.debug_toolbar.render('panels/sqlalchemy_select.html', {
|
||||
'result': result.fetchall(),
|
||||
'headers': result.keys(),
|
||||
'sql': format_sql(statement, params),
|
||||
'duration': float(request.args['duration']),
|
||||
})
|
||||
136
src/flask_debugtoolbar/panels/template.py
Normal file
@@ -0,0 +1,136 @@
|
||||
import collections
|
||||
import json
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
from flask import (
|
||||
template_rendered, request, g,
|
||||
Response, current_app, abort, url_for
|
||||
)
|
||||
from flask_debugtoolbar import module
|
||||
from flask_debugtoolbar.panels import DebugPanel
|
||||
|
||||
_ = lambda x: x
|
||||
|
||||
|
||||
class TemplateDebugPanel(DebugPanel):
|
||||
"""
|
||||
Panel that displays the time a response took in milliseconds.
|
||||
"""
|
||||
name = 'Template'
|
||||
has_content = True
|
||||
|
||||
# save the context for the 5 most recent requests
|
||||
template_cache = collections.deque(maxlen=5)
|
||||
|
||||
@classmethod
|
||||
def get_cache_for_key(self, key):
|
||||
for cache_key, value in self.template_cache:
|
||||
if key == cache_key:
|
||||
return value
|
||||
raise KeyError(key)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(self.__class__, self).__init__(*args, **kwargs)
|
||||
self.key = str(uuid.uuid4())
|
||||
self.templates = []
|
||||
template_rendered.connect(self._store_template_info)
|
||||
|
||||
def _store_template_info(self, sender, **kwargs):
|
||||
# only record in the cache if the editor is enabled and there is
|
||||
# actually a template for this request
|
||||
if not self.templates and is_editor_enabled():
|
||||
self.template_cache.append((self.key, self.templates))
|
||||
self.templates.append(kwargs)
|
||||
|
||||
def process_request(self, request):
|
||||
pass
|
||||
|
||||
def process_response(self, request, response):
|
||||
pass
|
||||
|
||||
def nav_title(self):
|
||||
return _('Templates')
|
||||
|
||||
def nav_subtitle(self):
|
||||
return "%d rendered" % len(self.templates)
|
||||
|
||||
def title(self):
|
||||
return _('Templates')
|
||||
|
||||
def url(self):
|
||||
return ''
|
||||
|
||||
def content(self):
|
||||
return self.render('panels/template.html', {
|
||||
'key': self.key,
|
||||
'templates': self.templates,
|
||||
'editable': is_editor_enabled(),
|
||||
})
|
||||
|
||||
|
||||
def is_editor_enabled():
|
||||
return current_app.config.get('DEBUG_TB_TEMPLATE_EDITOR_ENABLED')
|
||||
|
||||
|
||||
def require_enabled():
|
||||
if not is_editor_enabled():
|
||||
abort(403)
|
||||
|
||||
|
||||
def _get_source(template):
|
||||
with open(template.filename, 'rb') as fp:
|
||||
source = fp.read()
|
||||
return source.decode(_template_encoding())
|
||||
|
||||
|
||||
def _template_encoding():
|
||||
return getattr(current_app.jinja_loader, 'encoding', 'utf-8')
|
||||
|
||||
|
||||
@module.route('/template/<key>')
|
||||
def template_editor(key):
|
||||
require_enabled()
|
||||
# TODO set up special loader that caches templates it loads
|
||||
# and can override template contents
|
||||
templates = [t['template'] for t in
|
||||
TemplateDebugPanel.get_cache_for_key(key)]
|
||||
return g.debug_toolbar.render('panels/template_editor.html', {
|
||||
'static_path': url_for('_debug_toolbar.static', filename=''),
|
||||
'request': request,
|
||||
'templates': [
|
||||
{'name': t.name, 'source': _get_source(t)}
|
||||
for t in templates
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
@module.route('/template/<key>/save', methods=['POST'])
|
||||
def save_template(key):
|
||||
require_enabled()
|
||||
template = TemplateDebugPanel.get_cache_for_key(key)[0]['template']
|
||||
content = request.form['content'].encode(_template_encoding())
|
||||
with open(template.filename, 'wb') as fp:
|
||||
fp.write(content)
|
||||
return 'ok'
|
||||
|
||||
|
||||
@module.route('/template/<key>', methods=['POST'])
|
||||
def template_preview(key):
|
||||
require_enabled()
|
||||
context = TemplateDebugPanel.get_cache_for_key(key)[0]['context']
|
||||
content = request.form['content']
|
||||
env = current_app.jinja_env.overlay(autoescape=True)
|
||||
try:
|
||||
template = env.from_string(content)
|
||||
return template.render(context)
|
||||
except Exception as e:
|
||||
tb = sys.exc_info()[2]
|
||||
try:
|
||||
while tb.tb_next:
|
||||
tb = tb.tb_next
|
||||
msg = {'lineno': tb.tb_lineno, 'error': str(e)}
|
||||
return Response(json.dumps(msg), status=400,
|
||||
mimetype='application/json')
|
||||
finally:
|
||||
del tb
|
||||
@@ -1,18 +1,19 @@
|
||||
try:
|
||||
import resource
|
||||
except ImportError:
|
||||
pass # Will fail on Win32 systems
|
||||
pass # Will fail on Win32 systems
|
||||
import time
|
||||
from flask_debugtoolbar.panels import DebugPanel
|
||||
|
||||
_ = lambda x: x
|
||||
|
||||
|
||||
class TimerDebugPanel(DebugPanel):
|
||||
"""
|
||||
Panel that displays the time a response took in milliseconds.
|
||||
"""
|
||||
name = 'Timer'
|
||||
try: # if resource module not available, don't show content panel
|
||||
try: # if resource module not available, don't show content panel
|
||||
resource
|
||||
except NameError:
|
||||
has_content = False
|
||||
@@ -36,13 +37,14 @@ class TimerDebugPanel(DebugPanel):
|
||||
|
||||
def nav_subtitle(self):
|
||||
# TODO l10n
|
||||
if self.has_resource:
|
||||
utime = self._end_rusage.ru_utime - self._start_rusage.ru_utime
|
||||
stime = self._end_rusage.ru_stime - self._start_rusage.ru_stime
|
||||
return 'CPU: %0.2fms (%0.2fms)' % ((utime + stime) * 1000.0, self.total_time)
|
||||
else:
|
||||
if not self.has_resource:
|
||||
return 'TOTAL: %0.2fms' % (self.total_time)
|
||||
|
||||
utime = self._end_rusage.ru_utime - self._start_rusage.ru_utime
|
||||
stime = self._end_rusage.ru_stime - self._start_rusage.ru_stime
|
||||
return 'CPU: %0.2fms (%0.2fms)' % (
|
||||
(utime + stime) * 1000.0, self.total_time)
|
||||
|
||||
def title(self):
|
||||
return _('Resource Usage')
|
||||
|
||||
@@ -50,7 +52,7 @@ class TimerDebugPanel(DebugPanel):
|
||||
return ''
|
||||
|
||||
def _elapsed_ru(self, name):
|
||||
return getattr(self._end_rusage, name) - getattr(self._start_rusage, name)
|
||||
return (getattr(self._end_rusage, name) - getattr(self._start_rusage, name))
|
||||
|
||||
def content(self):
|
||||
|
||||
@@ -58,8 +60,8 @@ class TimerDebugPanel(DebugPanel):
|
||||
stime = 1000 * self._elapsed_ru('ru_stime')
|
||||
vcsw = self._elapsed_ru('ru_nvcsw')
|
||||
ivcsw = self._elapsed_ru('ru_nivcsw')
|
||||
minflt = self._elapsed_ru('ru_minflt')
|
||||
majflt = self._elapsed_ru('ru_majflt')
|
||||
# minflt = self._elapsed_ru('ru_minflt')
|
||||
# majflt = self._elapsed_ru('ru_majflt')
|
||||
|
||||
# these are documented as not meaningful under Linux. If you're running BSD
|
||||
# feel free to enable them, and add any others that I hadn't gotten to before
|
||||
@@ -80,9 +82,9 @@ class TimerDebugPanel(DebugPanel):
|
||||
(_('Total CPU time'), '%0.3f msec' % (utime + stime)),
|
||||
(_('Elapsed time'), '%0.3f msec' % self.total_time),
|
||||
(_('Context switches'), '%d voluntary, %d involuntary' % (vcsw, ivcsw)),
|
||||
# ('Memory use', '%d max RSS, %d shared, %d unshared' % (rss, srss, urss + usrss)),
|
||||
# ('Page faults', '%d no i/o, %d requiring i/o' % (minflt, majflt)),
|
||||
# ('Disk operations', '%d in, %d out, %d swapout' % (blkin, blkout, swap)),
|
||||
# ('Memory use', '%d max RSS, %d shared, %d unshared' % (rss, srss, urss + usrss)),
|
||||
# ('Page faults', '%d no i/o, %d requiring i/o' % (minflt, majflt)),
|
||||
# ('Disk operations', '%d in, %d out, %d swapout' % (blkin, blkout, swap)),
|
||||
)
|
||||
|
||||
context = self.context.copy()
|
||||
@@ -91,4 +93,3 @@ class TimerDebugPanel(DebugPanel):
|
||||
})
|
||||
|
||||
return self.render('panels/timer.html', context)
|
||||
|
||||
51
src/flask_debugtoolbar/panels/versions.py
Normal file
@@ -0,0 +1,51 @@
|
||||
import os
|
||||
from sysconfig import get_path
|
||||
|
||||
from flask_debugtoolbar.panels import DebugPanel
|
||||
|
||||
try:
|
||||
# Python 3.8+
|
||||
from importlib.metadata import version
|
||||
|
||||
flask_version = version('flask')
|
||||
|
||||
except ImportError:
|
||||
import pkg_resources
|
||||
|
||||
flask_version = pkg_resources.get_distribution('flask').version
|
||||
|
||||
_ = lambda x: x
|
||||
|
||||
|
||||
class VersionDebugPanel(DebugPanel):
|
||||
"""
|
||||
Panel that displays the Flask version.
|
||||
"""
|
||||
name = 'Version'
|
||||
has_content = True
|
||||
|
||||
def nav_title(self):
|
||||
return _('Versions')
|
||||
|
||||
def nav_subtitle(self):
|
||||
return 'Flask %s' % flask_version
|
||||
|
||||
def url(self):
|
||||
return ''
|
||||
|
||||
def title(self):
|
||||
return _('Versions')
|
||||
|
||||
def content(self):
|
||||
try:
|
||||
import importlib.metadata
|
||||
except ImportError:
|
||||
packages = []
|
||||
else:
|
||||
packages_metadata = [p.metadata for p in importlib.metadata.distributions()]
|
||||
packages = sorted(packages_metadata, key=lambda p: p['Name'].lower())
|
||||
|
||||
return self.render('panels/versions.html', {
|
||||
'packages': packages,
|
||||
'python_lib_dir': os.path.normpath(get_path('platlib')),
|
||||
})
|
||||
112
src/flask_debugtoolbar/static/codemirror/codemirror.css
Normal file
@@ -0,0 +1,112 @@
|
||||
.CodeMirror {
|
||||
line-height: 1em;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.CodeMirror-scroll {
|
||||
overflow: auto;
|
||||
height: 300px;
|
||||
/* This is needed to prevent an IE[67] bug where the scrolled content
|
||||
is visible outside of the scrolling box. */
|
||||
position: relative;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.CodeMirror-gutter {
|
||||
position: absolute; left: 0; top: 0;
|
||||
z-index: 10;
|
||||
background-color: #f7f7f7;
|
||||
border-right: 1px solid #eee;
|
||||
min-width: 2em;
|
||||
height: 100%;
|
||||
}
|
||||
.CodeMirror-gutter-text {
|
||||
color: #aaa;
|
||||
text-align: right;
|
||||
padding: .4em .2em .4em .4em;
|
||||
white-space: pre !important;
|
||||
}
|
||||
.CodeMirror-lines {
|
||||
padding: .4em;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.CodeMirror pre {
|
||||
-moz-border-radius: 0;
|
||||
-webkit-border-radius: 0;
|
||||
-o-border-radius: 0;
|
||||
border-radius: 0;
|
||||
border-width: 0; margin: 0; padding: 0; background: transparent;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
padding: 0; margin: 0;
|
||||
white-space: pre;
|
||||
word-wrap: normal;
|
||||
}
|
||||
|
||||
.CodeMirror-wrap pre {
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.CodeMirror-wrap .CodeMirror-scroll {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.CodeMirror textarea {
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
.CodeMirror pre.CodeMirror-cursor {
|
||||
z-index: 10;
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
border-left: 1px solid black;
|
||||
border-right:none;
|
||||
width:0;
|
||||
}
|
||||
.CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {}
|
||||
.CodeMirror-focused pre.CodeMirror-cursor {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
div.CodeMirror-selected { background: #d9d9d9; }
|
||||
.CodeMirror-focused div.CodeMirror-selected { background: #d7d4f0; }
|
||||
|
||||
.CodeMirror-searching {
|
||||
background: #ffa;
|
||||
background: rgba(255, 255, 0, .4);
|
||||
}
|
||||
|
||||
/* Default theme */
|
||||
|
||||
.cm-s-default span.cm-keyword {color: #708;}
|
||||
.cm-s-default span.cm-atom {color: #219;}
|
||||
.cm-s-default span.cm-number {color: #164;}
|
||||
.cm-s-default span.cm-def {color: #00f;}
|
||||
.cm-s-default span.cm-variable {color: black;}
|
||||
.cm-s-default span.cm-variable-2 {color: #05a;}
|
||||
.cm-s-default span.cm-variable-3 {color: #085;}
|
||||
.cm-s-default span.cm-property {color: black;}
|
||||
.cm-s-default span.cm-operator {color: black;}
|
||||
.cm-s-default span.cm-comment {color: #a50;}
|
||||
.cm-s-default span.cm-string {color: #a11;}
|
||||
.cm-s-default span.cm-string-2 {color: #f50;}
|
||||
.cm-s-default span.cm-meta {color: #555;}
|
||||
.cm-s-default span.cm-error {color: #f00;}
|
||||
.cm-s-default span.cm-qualifier {color: #555;}
|
||||
.cm-s-default span.cm-builtin {color: #30a;}
|
||||
.cm-s-default span.cm-bracket {color: #cc7;}
|
||||
.cm-s-default span.cm-tag {color: #170;}
|
||||
.cm-s-default span.cm-attribute {color: #00c;}
|
||||
.cm-s-default span.cm-header {color: #a0a;}
|
||||
.cm-s-default span.cm-quote {color: #090;}
|
||||
.cm-s-default span.cm-hr {color: #999;}
|
||||
.cm-s-default span.cm-link {color: #00c;}
|
||||
|
||||
span.cm-header, span.cm-strong {font-weight: bold;}
|
||||
span.cm-em {font-style: italic;}
|
||||
span.cm-emstrong {font-style: italic; font-weight: bold;}
|
||||
span.cm-link {text-decoration: underline;}
|
||||
|
||||
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
|
||||
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
|
||||
2972
src/flask_debugtoolbar/static/codemirror/codemirror.js
Normal file
234
src/flask_debugtoolbar/static/codemirror/mode/clike/clike.js
vendored
Normal file
@@ -0,0 +1,234 @@
|
||||
CodeMirror.defineMode("clike", function(config, parserConfig) {
|
||||
var indentUnit = config.indentUnit,
|
||||
keywords = parserConfig.keywords || {},
|
||||
blockKeywords = parserConfig.blockKeywords || {},
|
||||
atoms = parserConfig.atoms || {},
|
||||
hooks = parserConfig.hooks || {},
|
||||
multiLineStrings = parserConfig.multiLineStrings;
|
||||
var isOperatorChar = /[+\-*&%=<>!?|\/]/;
|
||||
|
||||
var curPunc;
|
||||
|
||||
function tokenBase(stream, state) {
|
||||
var ch = stream.next();
|
||||
if (hooks[ch]) {
|
||||
var result = hooks[ch](stream, state);
|
||||
if (result !== false) return result;
|
||||
}
|
||||
if (ch == '"' || ch == "'") {
|
||||
state.tokenize = tokenString(ch);
|
||||
return state.tokenize(stream, state);
|
||||
}
|
||||
if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
|
||||
curPunc = ch;
|
||||
return null
|
||||
}
|
||||
if (/\d/.test(ch)) {
|
||||
stream.eatWhile(/[\w\.]/);
|
||||
return "number";
|
||||
}
|
||||
if (ch == "/") {
|
||||
if (stream.eat("*")) {
|
||||
state.tokenize = tokenComment;
|
||||
return tokenComment(stream, state);
|
||||
}
|
||||
if (stream.eat("/")) {
|
||||
stream.skipToEnd();
|
||||
return "comment";
|
||||
}
|
||||
}
|
||||
if (isOperatorChar.test(ch)) {
|
||||
stream.eatWhile(isOperatorChar);
|
||||
return "operator";
|
||||
}
|
||||
stream.eatWhile(/[\w\$_]/);
|
||||
var cur = stream.current();
|
||||
if (keywords.propertyIsEnumerable(cur)) {
|
||||
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
|
||||
return "keyword";
|
||||
}
|
||||
if (atoms.propertyIsEnumerable(cur)) return "atom";
|
||||
return "word";
|
||||
}
|
||||
|
||||
function tokenString(quote) {
|
||||
return function(stream, state) {
|
||||
var escaped = false, next, end = false;
|
||||
while ((next = stream.next()) != null) {
|
||||
if (next == quote && !escaped) {end = true; break;}
|
||||
escaped = !escaped && next == "\\";
|
||||
}
|
||||
if (end || !(escaped || multiLineStrings))
|
||||
state.tokenize = null;
|
||||
return "string";
|
||||
};
|
||||
}
|
||||
|
||||
function tokenComment(stream, state) {
|
||||
var maybeEnd = false, ch;
|
||||
while (ch = stream.next()) {
|
||||
if (ch == "/" && maybeEnd) {
|
||||
state.tokenize = null;
|
||||
break;
|
||||
}
|
||||
maybeEnd = (ch == "*");
|
||||
}
|
||||
return "comment";
|
||||
}
|
||||
|
||||
function Context(indented, column, type, align, prev) {
|
||||
this.indented = indented;
|
||||
this.column = column;
|
||||
this.type = type;
|
||||
this.align = align;
|
||||
this.prev = prev;
|
||||
}
|
||||
function pushContext(state, col, type) {
|
||||
return state.context = new Context(state.indented, col, type, null, state.context);
|
||||
}
|
||||
function popContext(state) {
|
||||
var t = state.context.type;
|
||||
if (t == ")" || t == "]" || t == "}")
|
||||
state.indented = state.context.indented;
|
||||
return state.context = state.context.prev;
|
||||
}
|
||||
|
||||
// Interface
|
||||
|
||||
return {
|
||||
startState: function(basecolumn) {
|
||||
return {
|
||||
tokenize: null,
|
||||
context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
|
||||
indented: 0,
|
||||
startOfLine: true
|
||||
};
|
||||
},
|
||||
|
||||
token: function(stream, state) {
|
||||
var ctx = state.context;
|
||||
if (stream.sol()) {
|
||||
if (ctx.align == null) ctx.align = false;
|
||||
state.indented = stream.indentation();
|
||||
state.startOfLine = true;
|
||||
}
|
||||
if (stream.eatSpace()) return null;
|
||||
curPunc = null;
|
||||
var style = (state.tokenize || tokenBase)(stream, state);
|
||||
if (style == "comment" || style == "meta") return style;
|
||||
if (ctx.align == null) ctx.align = true;
|
||||
|
||||
if ((curPunc == ";" || curPunc == ":") && ctx.type == "statement") popContext(state);
|
||||
else if (curPunc == "{") pushContext(state, stream.column(), "}");
|
||||
else if (curPunc == "[") pushContext(state, stream.column(), "]");
|
||||
else if (curPunc == "(") pushContext(state, stream.column(), ")");
|
||||
else if (curPunc == "}") {
|
||||
while (ctx.type == "statement") ctx = popContext(state);
|
||||
if (ctx.type == "}") ctx = popContext(state);
|
||||
while (ctx.type == "statement") ctx = popContext(state);
|
||||
}
|
||||
else if (curPunc == ctx.type) popContext(state);
|
||||
else if (ctx.type == "}" || ctx.type == "top" || (ctx.type == "statement" && curPunc == "newstatement"))
|
||||
pushContext(state, stream.column(), "statement");
|
||||
state.startOfLine = false;
|
||||
return style;
|
||||
},
|
||||
|
||||
indent: function(state, textAfter) {
|
||||
if (state.tokenize != tokenBase && state.tokenize != null) return 0;
|
||||
var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
|
||||
if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
|
||||
var closing = firstChar == ctx.type;
|
||||
if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : indentUnit);
|
||||
else if (ctx.align) return ctx.column + (closing ? 0 : 1);
|
||||
else return ctx.indented + (closing ? 0 : indentUnit);
|
||||
},
|
||||
|
||||
electricChars: "{}"
|
||||
};
|
||||
});
|
||||
|
||||
(function() {
|
||||
function words(str) {
|
||||
var obj = {}, words = str.split(" ");
|
||||
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
|
||||
return obj;
|
||||
}
|
||||
var cKeywords = "auto if break int case long char register continue return default short do sizeof " +
|
||||
"double static else struct entry switch extern typedef float union for unsigned " +
|
||||
"goto while enum void const signed volatile";
|
||||
|
||||
function cppHook(stream, state) {
|
||||
if (!state.startOfLine) return false;
|
||||
stream.skipToEnd();
|
||||
return "meta";
|
||||
}
|
||||
|
||||
// C#-style strings where "" escapes a quote.
|
||||
function tokenAtString(stream, state) {
|
||||
var next;
|
||||
while ((next = stream.next()) != null) {
|
||||
if (next == '"' && !stream.eat('"')) {
|
||||
state.tokenize = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return "string";
|
||||
}
|
||||
|
||||
CodeMirror.defineMIME("text/x-csrc", {
|
||||
name: "clike",
|
||||
keywords: words(cKeywords),
|
||||
blockKeywords: words("case do else for if switch while struct"),
|
||||
atoms: words("null"),
|
||||
hooks: {"#": cppHook}
|
||||
});
|
||||
CodeMirror.defineMIME("text/x-c++src", {
|
||||
name: "clike",
|
||||
keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try bool explicit new " +
|
||||
"static_cast typeid catch operator template typename class friend private " +
|
||||
"this using const_cast inline public throw virtual delete mutable protected " +
|
||||
"wchar_t"),
|
||||
blockKeywords: words("catch class do else finally for if struct switch try while"),
|
||||
atoms: words("true false null"),
|
||||
hooks: {"#": cppHook}
|
||||
});
|
||||
CodeMirror.defineMIME("text/x-java", {
|
||||
name: "clike",
|
||||
keywords: words("abstract assert boolean break byte case catch char class const continue default " +
|
||||
"do double else enum extends final finally float for goto if implements import " +
|
||||
"instanceof int interface long native new package private protected public " +
|
||||
"return short static strictfp super switch synchronized this throw throws transient " +
|
||||
"try void volatile while"),
|
||||
blockKeywords: words("catch class do else finally for if switch try while"),
|
||||
atoms: words("true false null"),
|
||||
hooks: {
|
||||
"@": function(stream, state) {
|
||||
stream.eatWhile(/[\w\$_]/);
|
||||
return "meta";
|
||||
}
|
||||
}
|
||||
});
|
||||
CodeMirror.defineMIME("text/x-csharp", {
|
||||
name: "clike",
|
||||
keywords: words("abstract as base bool break byte case catch char checked class const continue decimal" +
|
||||
" default delegate do double else enum event explicit extern finally fixed float for" +
|
||||
" foreach goto if implicit in int interface internal is lock long namespace new object" +
|
||||
" operator out override params private protected public readonly ref return sbyte sealed short" +
|
||||
" sizeof stackalloc static string struct switch this throw try typeof uint ulong unchecked" +
|
||||
" unsafe ushort using virtual void volatile while add alias ascending descending dynamic from get" +
|
||||
" global group into join let orderby partial remove select set value var yield"),
|
||||
blockKeywords: words("catch class do else finally for foreach if struct switch try while"),
|
||||
atoms: words("true false null"),
|
||||
hooks: {
|
||||
"@": function(stream, state) {
|
||||
if (stream.eat('"')) {
|
||||
state.tokenize = tokenAtString;
|
||||
return tokenAtString(stream, state);
|
||||
}
|
||||
stream.eatWhile(/[\w\$_]/);
|
||||
return "meta";
|
||||
}
|
||||
}
|
||||
});
|
||||
}());
|
||||
101
src/flask_debugtoolbar/static/codemirror/mode/clike/index.html
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CodeMirror: C-like mode</title>
|
||||
<link rel="stylesheet" href="../../lib/codemirror.css">
|
||||
<script src="../../lib/codemirror.js"></script>
|
||||
<script src="clike.js"></script>
|
||||
<link rel="stylesheet" href="../../doc/docs.css">
|
||||
<style>.CodeMirror {border: 2px inset #dee;}</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>CodeMirror: C-like mode</h1>
|
||||
|
||||
<form><textarea id="code" name="code">
|
||||
/* C demo code */
|
||||
|
||||
#include <zmq.h>
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <malloc.h>
|
||||
|
||||
typedef struct {
|
||||
void* arg_socket;
|
||||
zmq_msg_t* arg_msg;
|
||||
char* arg_string;
|
||||
unsigned long arg_len;
|
||||
int arg_int, arg_command;
|
||||
|
||||
int signal_fd;
|
||||
int pad;
|
||||
void* context;
|
||||
sem_t sem;
|
||||
} acl_zmq_context;
|
||||
|
||||
#define p(X) (context->arg_##X)
|
||||
|
||||
void* zmq_thread(void* context_pointer) {
|
||||
acl_zmq_context* context = (acl_zmq_context*)context_pointer;
|
||||
char ok = 'K', err = 'X';
|
||||
int res;
|
||||
|
||||
while (1) {
|
||||
while ((res = sem_wait(&context->sem)) == EINTR);
|
||||
if (res) {write(context->signal_fd, &err, 1); goto cleanup;}
|
||||
switch(p(command)) {
|
||||
case 0: goto cleanup;
|
||||
case 1: p(socket) = zmq_socket(context->context, p(int)); break;
|
||||
case 2: p(int) = zmq_close(p(socket)); break;
|
||||
case 3: p(int) = zmq_bind(p(socket), p(string)); break;
|
||||
case 4: p(int) = zmq_connect(p(socket), p(string)); break;
|
||||
case 5: p(int) = zmq_getsockopt(p(socket), p(int), (void*)p(string), &p(len)); break;
|
||||
case 6: p(int) = zmq_setsockopt(p(socket), p(int), (void*)p(string), p(len)); break;
|
||||
case 7: p(int) = zmq_send(p(socket), p(msg), p(int)); break;
|
||||
case 8: p(int) = zmq_recv(p(socket), p(msg), p(int)); break;
|
||||
case 9: p(int) = zmq_poll(p(socket), p(int), p(len)); break;
|
||||
}
|
||||
p(command) = errno;
|
||||
write(context->signal_fd, &ok, 1);
|
||||
}
|
||||
cleanup:
|
||||
close(context->signal_fd);
|
||||
free(context_pointer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* zmq_thread_init(void* zmq_context, int signal_fd) {
|
||||
acl_zmq_context* context = malloc(sizeof(acl_zmq_context));
|
||||
pthread_t thread;
|
||||
|
||||
context->context = zmq_context;
|
||||
context->signal_fd = signal_fd;
|
||||
sem_init(&context->sem, 1, 0);
|
||||
pthread_create(&thread, 0, &zmq_thread, context);
|
||||
pthread_detach(thread);
|
||||
return context;
|
||||
}
|
||||
</textarea></form>
|
||||
|
||||
<script>
|
||||
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
||||
lineNumbers: true,
|
||||
matchBrackets: true,
|
||||
mode: "text/x-csrc"
|
||||
});
|
||||
</script>
|
||||
|
||||
<p>Simple mode that tries to handle C-like languages as well as it
|
||||
can. Takes two configuration parameters: <code>keywords</code>, an
|
||||
object whose property names are the keywords in the language,
|
||||
and <code>useCPP</code>, which determines whether C preprocessor
|
||||
directives are recognized.</p>
|
||||
|
||||
<p><strong>MIME types defined:</strong> <code>text/x-csrc</code>
|
||||
(C code), <code>text/x-c++src</code> (C++
|
||||
code), <code>text/x-java</code> (Java
|
||||
code), <code>text/x-csharp</code> (C#).</p>
|
||||
</body>
|
||||
</html>
|
||||
207
src/flask_debugtoolbar/static/codemirror/mode/clojure/clojure.js
vendored
Normal file
@@ -0,0 +1,207 @@
|
||||
/**
|
||||
* Author: Hans Engel
|
||||
* Branched from CodeMirror's Scheme mode (by Koh Zi Han, based on implementation by Koh Zi Chun)
|
||||
*/
|
||||
CodeMirror.defineMode("clojure", function (config, mode) {
|
||||
var BUILTIN = "builtin", COMMENT = "comment", STRING = "string", TAG = "tag",
|
||||
ATOM = "atom", NUMBER = "number", BRACKET = "bracket", KEYWORD = "keyword";
|
||||
var INDENT_WORD_SKIP = 2, KEYWORDS_SKIP = 1;
|
||||
|
||||
function makeKeywords(str) {
|
||||
var obj = {}, words = str.split(" ");
|
||||
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
|
||||
return obj;
|
||||
}
|
||||
|
||||
var atoms = makeKeywords("true false nil");
|
||||
|
||||
var keywords = makeKeywords(
|
||||
"defn defn- def def- defonce defmulti defmethod defmacro defstruct deftype defprotocol defrecord defproject deftest slice defalias defhinted defmacro- defn-memo defnk defnk defonce- defunbound defunbound- defvar defvar- let letfn do case cond condp for loop recur when when-not when-let when-first if if-let if-not . .. -> ->> doto and or dosync doseq dotimes dorun doall load import unimport ns in-ns refer try catch finally throw with-open with-local-vars binding gen-class gen-and-load-class gen-and-save-class handler-case handle");
|
||||
|
||||
var builtins = makeKeywords(
|
||||
"* *1 *2 *3 *agent* *allow-unresolved-vars* *assert *clojure-version* *command-line-args* *compile-files* *compile-path* *e *err* *file* *flush-on-newline* *in* *macro-meta* *math-context* *ns* *out* *print-dup* *print-length* *print-level* *print-meta* *print-readably* *read-eval* *source-path* *use-context-classloader* *warn-on-reflection* + - / < <= = == > >= accessor aclone agent agent-errors aget alength alias all-ns alter alter-meta! alter-var-root amap ancestors and apply areduce array-map aset aset-boolean aset-byte aset-char aset-double aset-float aset-int aset-long aset-short assert assoc assoc! assoc-in associative? atom await await-for await1 bases bean bigdec bigint binding bit-and bit-and-not bit-clear bit-flip bit-not bit-or bit-set bit-shift-left bit-shift-right bit-test bit-xor boolean boolean-array booleans bound-fn bound-fn* butlast byte byte-array bytes case cast char char-array char-escape-string char-name-string char? chars chunk chunk-append chunk-buffer chunk-cons chunk-first chunk-next chunk-rest chunked-seq? class class? clear-agent-errors clojure-version coll? comment commute comp comparator compare compare-and-set! compile complement concat cond condp conj conj! cons constantly construct-proxy contains? count counted? create-ns create-struct cycle dec decimal? declare definline defmacro defmethod defmulti defn defn- defonce defstruct delay delay? deliver deref derive descendants destructure disj disj! dissoc dissoc! distinct distinct? doall doc dorun doseq dosync dotimes doto double double-array doubles drop drop-last drop-while empty empty? ensure enumeration-seq eval even? every? extend extend-protocol extend-type extends? extenders false? ffirst file-seq filter find find-doc find-ns find-var first float float-array float? floats flush fn fn? fnext for force format future future-call future-cancel future-cancelled? future-done? future? gen-class gen-interface gensym get get-in get-method get-proxy-class get-thread-bindings get-validator hash hash-map hash-set identical? identity if-let if-not ifn? import in-ns inc init-proxy instance? int int-array integer? interleave intern interpose into into-array ints io! isa? iterate iterator-seq juxt key keys keyword keyword? last lazy-cat lazy-seq let letfn line-seq list list* list? load load-file load-reader load-string loaded-libs locking long long-array longs loop macroexpand macroexpand-1 make-array make-hierarchy map map? mapcat max max-key memfn memoize merge merge-with meta method-sig methods min min-key mod name namespace neg? newline next nfirst nil? nnext not not-any? not-empty not-every? not= ns ns-aliases ns-imports ns-interns ns-map ns-name ns-publics ns-refers ns-resolve ns-unalias ns-unmap nth nthnext num number? odd? or parents partial partition pcalls peek persistent! pmap pop pop! pop-thread-bindings pos? pr pr-str prefer-method prefers primitives-classnames print print-ctor print-doc print-dup print-method print-namespace-doc print-simple print-special-doc print-str printf println println-str prn prn-str promise proxy proxy-call-with-super proxy-mappings proxy-name proxy-super push-thread-bindings pvalues quot rand rand-int range ratio? rational? rationalize re-find re-groups re-matcher re-matches re-pattern re-seq read read-line read-string reify reduce ref ref-history-count ref-max-history ref-min-history ref-set refer refer-clojure release-pending-sends rem remove remove-method remove-ns repeat repeatedly replace replicate require reset! reset-meta! resolve rest resultset-seq reverse reversible? rseq rsubseq satisfies? second select-keys send send-off seq seq? seque sequence sequential? set set-validator! set? short short-array shorts shutdown-agents slurp some sort sort-by sorted-map sorted-map-by sorted-set sorted-set-by sorted? special-form-anchor special-symbol? split-at split-with str stream? string? struct struct-map subs subseq subvec supers swap! symbol symbol? sync syntax-symbol-anchor take take-last take-nth take-while test the-ns time to-array to-array-2d trampoline transient tree-seq true? type unchecked-add unchecked-dec unchecked-divide unchecked-inc unchecked-multiply unchecked-negate unchecked-remainder unchecked-subtract underive unquote unquote-splicing update-in update-proxy use val vals var-get var-set var? vary-meta vec vector vector? when when-first when-let when-not while with-bindings with-bindings* with-in-str with-loading-context with-local-vars with-meta with-open with-out-str with-precision xml-seq");
|
||||
|
||||
var indentKeys = makeKeywords(
|
||||
// Built-ins
|
||||
"ns fn def defn defmethod bound-fn if if-not case condp when while when-not when-first do future comment doto locking proxy with-open with-precision reify deftype defrecord defprotocol extend extend-protocol extend-type try catch " +
|
||||
|
||||
// Binding forms
|
||||
"let letfn binding loop for doseq dotimes when-let if-let " +
|
||||
|
||||
// Data structures
|
||||
"defstruct struct-map assoc " +
|
||||
|
||||
// clojure.test
|
||||
"testing deftest " +
|
||||
|
||||
// contrib
|
||||
"handler-case handle dotrace deftrace");
|
||||
|
||||
var tests = {
|
||||
digit: /\d/,
|
||||
digit_or_colon: /[\d:]/,
|
||||
hex: /[0-9a-fA-F]/,
|
||||
sign: /[+-]/,
|
||||
exponent: /[eE]/,
|
||||
keyword_char: /[^\s\(\[\;\)\]]/,
|
||||
basic: /[\w\$_\-]/,
|
||||
lang_keyword: /[\w*+!\-_?:\/]/
|
||||
};
|
||||
|
||||
function stateStack(indent, type, prev) { // represents a state stack object
|
||||
this.indent = indent;
|
||||
this.type = type;
|
||||
this.prev = prev;
|
||||
}
|
||||
|
||||
function pushStack(state, indent, type) {
|
||||
state.indentStack = new stateStack(indent, type, state.indentStack);
|
||||
}
|
||||
|
||||
function popStack(state) {
|
||||
state.indentStack = state.indentStack.prev;
|
||||
}
|
||||
|
||||
function isNumber(ch, stream){
|
||||
// hex
|
||||
if ( ch === '0' && 'x' == stream.peek().toLowerCase() ) {
|
||||
stream.eat('x');
|
||||
stream.eatWhile(tests.hex);
|
||||
return true;
|
||||
}
|
||||
|
||||
// leading sign
|
||||
if ( ch == '+' || ch == '-' ) {
|
||||
stream.eat(tests.sign);
|
||||
ch = stream.next();
|
||||
}
|
||||
|
||||
if ( tests.digit.test(ch) ) {
|
||||
stream.eat(ch);
|
||||
stream.eatWhile(tests.digit);
|
||||
|
||||
if ( '.' == stream.peek() ) {
|
||||
stream.eat('.');
|
||||
stream.eatWhile(tests.digit);
|
||||
}
|
||||
|
||||
if ( 'e' == stream.peek().toLowerCase() ) {
|
||||
stream.eat(tests.exponent);
|
||||
stream.eat(tests.sign);
|
||||
stream.eatWhile(tests.digit);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return {
|
||||
startState: function () {
|
||||
return {
|
||||
indentStack: null,
|
||||
indentation: 0,
|
||||
mode: false
|
||||
};
|
||||
},
|
||||
|
||||
token: function (stream, state) {
|
||||
if (state.indentStack == null && stream.sol()) {
|
||||
// update indentation, but only if indentStack is empty
|
||||
state.indentation = stream.indentation();
|
||||
}
|
||||
|
||||
// skip spaces
|
||||
if (stream.eatSpace()) {
|
||||
return null;
|
||||
}
|
||||
var returnType = null;
|
||||
|
||||
switch(state.mode){
|
||||
case "string": // multi-line string parsing mode
|
||||
var next, escaped = false;
|
||||
while ((next = stream.next()) != null) {
|
||||
if (next == "\"" && !escaped) {
|
||||
|
||||
state.mode = false;
|
||||
break;
|
||||
}
|
||||
escaped = !escaped && next == "\\";
|
||||
}
|
||||
returnType = STRING; // continue on in string mode
|
||||
break;
|
||||
default: // default parsing mode
|
||||
var ch = stream.next();
|
||||
|
||||
if (ch == "\"") {
|
||||
state.mode = "string";
|
||||
returnType = STRING;
|
||||
} else if (ch == "'" && !( tests.digit_or_colon.test(stream.peek()) )) {
|
||||
returnType = ATOM;
|
||||
} else if (ch == ";") { // comment
|
||||
stream.skipToEnd(); // rest of the line is a comment
|
||||
returnType = COMMENT;
|
||||
} else if (isNumber(ch,stream)){
|
||||
returnType = NUMBER;
|
||||
} else if (ch == "(" || ch == "[") {
|
||||
var keyWord = ''; var indentTemp = stream.column();
|
||||
/**
|
||||
Either
|
||||
(indent-word ..
|
||||
(non-indent-word ..
|
||||
(;something else, bracket, etc.
|
||||
*/
|
||||
|
||||
if (ch == "(") while ((letter = stream.eat(tests.keyword_char)) != null) {
|
||||
keyWord += letter;
|
||||
}
|
||||
|
||||
if (keyWord.length > 0 && indentKeys.propertyIsEnumerable(keyWord)) { // indent-word
|
||||
pushStack(state, indentTemp + INDENT_WORD_SKIP, ch);
|
||||
} else { // non-indent word
|
||||
// we continue eating the spaces
|
||||
stream.eatSpace();
|
||||
if (stream.eol() || stream.peek() == ";") {
|
||||
// nothing significant after
|
||||
// we restart indentation 1 space after
|
||||
pushStack(state, indentTemp + 1, ch);
|
||||
} else {
|
||||
pushStack(state, indentTemp + stream.current().length, ch); // else we match
|
||||
}
|
||||
}
|
||||
stream.backUp(stream.current().length - 1); // undo all the eating
|
||||
|
||||
returnType = BRACKET;
|
||||
} else if (ch == ")" || ch == "]") {
|
||||
returnType = BRACKET;
|
||||
if (state.indentStack != null && state.indentStack.type == (ch == ")" ? "(" : "[")) {
|
||||
popStack(state);
|
||||
}
|
||||
} else if ( ch == ":" ) {
|
||||
stream.eatWhile(tests.lang_keyword);
|
||||
return ATOM;
|
||||
} else {
|
||||
stream.eatWhile(tests.basic);
|
||||
|
||||
if (keywords && keywords.propertyIsEnumerable(stream.current())) {
|
||||
returnType = KEYWORD;
|
||||
} else if (builtins && builtins.propertyIsEnumerable(stream.current())) {
|
||||
returnType = BUILTIN;
|
||||
} else if (atoms && atoms.propertyIsEnumerable(stream.current())) {
|
||||
returnType = ATOM;
|
||||
} else returnType = null;
|
||||
}
|
||||
}
|
||||
|
||||
return returnType;
|
||||
},
|
||||
|
||||
indent: function (state, textAfter) {
|
||||
if (state.indentStack == null) return state.indentation;
|
||||
return state.indentStack.indent;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
CodeMirror.defineMIME("text/x-clojure", "clojure");
|
||||
66
src/flask_debugtoolbar/static/codemirror/mode/clojure/index.html
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CodeMirror: Clojure mode</title>
|
||||
<link rel="stylesheet" href="../../lib/codemirror.css">
|
||||
<script src="../../lib/codemirror.js"></script>
|
||||
<script src="clojure.js"></script>
|
||||
<style>.CodeMirror {background: #f8f8f8;}</style>
|
||||
<link rel="stylesheet" href="../../doc/docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>CodeMirror: Clojure mode</h1>
|
||||
<form><textarea id="code" name="code">
|
||||
; Conway's Game of Life, based on the work of:
|
||||
;; Laurent Petit https://gist.github.com/1200343
|
||||
;; Christophe Grand http://clj-me.cgrand.net/2011/08/19/conways-game-of-life
|
||||
|
||||
(ns ^{:doc "Conway's Game of Life."}
|
||||
game-of-life)
|
||||
|
||||
;; Core game of life's algorithm functions
|
||||
|
||||
(defn neighbours
|
||||
"Given a cell's coordinates, returns the coordinates of its neighbours."
|
||||
[[x y]]
|
||||
(for [dx [-1 0 1] dy (if (zero? dx) [-1 1] [-1 0 1])]
|
||||
[(+ dx x) (+ dy y)]))
|
||||
|
||||
(defn step
|
||||
"Given a set of living cells, computes the new set of living cells."
|
||||
[cells]
|
||||
(set (for [[cell n] (frequencies (mapcat neighbours cells))
|
||||
:when (or (= n 3) (and (= n 2) (cells cell)))]
|
||||
cell)))
|
||||
|
||||
;; Utility methods for displaying game on a text terminal
|
||||
|
||||
(defn print-board
|
||||
"Prints a board on *out*, representing a step in the game."
|
||||
[board w h]
|
||||
(doseq [x (range (inc w)) y (range (inc h))]
|
||||
(if (= y 0) (print "\n"))
|
||||
(print (if (board [x y]) "[X]" " . "))))
|
||||
|
||||
(defn display-grids
|
||||
"Prints a squence of boards on *out*, representing several steps."
|
||||
[grids w h]
|
||||
(doseq [board grids]
|
||||
(print-board board w h)
|
||||
(print "\n")))
|
||||
|
||||
;; Launches an example board
|
||||
|
||||
(def
|
||||
^{:doc "board represents the initial set of living cells"}
|
||||
board #{[2 1] [2 2] [2 3]})
|
||||
|
||||
(display-grids (take 3 (iterate step board)) 5 5) </textarea></form>
|
||||
<script>
|
||||
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {});
|
||||
</script>
|
||||
|
||||
<p><strong>MIME types defined:</strong> <code>text/x-clojure</code>.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
22
src/flask_debugtoolbar/static/codemirror/mode/coffeescript/LICENSE
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2011 Jeff Pickhardt
|
||||
Modified from the Python CodeMirror mode, Copyright (c) 2010 Timothy Farrell
|
||||
|
||||
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.
|
||||
341
src/flask_debugtoolbar/static/codemirror/mode/coffeescript/coffeescript.js
vendored
Normal file
@@ -0,0 +1,341 @@
|
||||
/**
|
||||
* Link to the project's GitHub page:
|
||||
* https://github.com/pickhardt/coffeescript-codemirror-mode
|
||||
*/
|
||||
CodeMirror.defineMode('coffeescript', function(conf) {
|
||||
var ERRORCLASS = 'error';
|
||||
|
||||
function wordRegexp(words) {
|
||||
return new RegExp("^((" + words.join(")|(") + "))\\b");
|
||||
}
|
||||
|
||||
var singleOperators = new RegExp("^[\\+\\-\\*/%&|\\^~<>!\?]");
|
||||
var singleDelimiters = new RegExp('^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]');
|
||||
var doubleOperators = new RegExp("^((\->)|(\=>)|(\\+\\+)|(\\+\\=)|(\\-\\-)|(\\-\\=)|(\\*\\*)|(\\*\\=)|(\\/\\/)|(\\/\\=)|(==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//))");
|
||||
var doubleDelimiters = new RegExp("^((\\.\\.)|(\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))");
|
||||
var tripleDelimiters = new RegExp("^((\\.\\.\\.)|(//=)|(>>=)|(<<=)|(\\*\\*=))");
|
||||
var identifiers = new RegExp("^[_A-Za-z$][_A-Za-z$0-9]*");
|
||||
|
||||
var wordOperators = wordRegexp(['and', 'or', 'not',
|
||||
'is', 'isnt', 'in',
|
||||
'instanceof', 'typeof']);
|
||||
var indentKeywords = ['for', 'while', 'loop', 'if', 'unless', 'else',
|
||||
'switch', 'try', 'catch', 'finally', 'class'];
|
||||
var commonKeywords = ['break', 'by', 'continue', 'debugger', 'delete',
|
||||
'do', 'in', 'of', 'new', 'return', 'then',
|
||||
'this', 'throw', 'when', 'until'];
|
||||
|
||||
var keywords = wordRegexp(indentKeywords.concat(commonKeywords));
|
||||
|
||||
indentKeywords = wordRegexp(indentKeywords);
|
||||
|
||||
|
||||
var stringPrefixes = new RegExp("^('{3}|\"{3}|['\"])");
|
||||
var regexPrefixes = new RegExp("^(/{3}|/)");
|
||||
var commonConstants = ['Infinity', 'NaN', 'undefined', 'null', 'true', 'false', 'on', 'off', 'yes', 'no'];
|
||||
var constants = wordRegexp(commonConstants);
|
||||
|
||||
// Tokenizers
|
||||
function tokenBase(stream, state) {
|
||||
// Handle scope changes
|
||||
if (stream.sol()) {
|
||||
var scopeOffset = state.scopes[0].offset;
|
||||
if (stream.eatSpace()) {
|
||||
var lineOffset = stream.indentation();
|
||||
if (lineOffset > scopeOffset) {
|
||||
return 'indent';
|
||||
} else if (lineOffset < scopeOffset) {
|
||||
return 'dedent';
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
if (scopeOffset > 0) {
|
||||
dedent(stream, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (stream.eatSpace()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var ch = stream.peek();
|
||||
|
||||
// Handle multi line comments
|
||||
if (stream.match("###")) {
|
||||
state.tokenize = longComment;
|
||||
return state.tokenize(stream, state);
|
||||
}
|
||||
|
||||
// Single line comment
|
||||
if (ch === '#') {
|
||||
stream.skipToEnd();
|
||||
return 'comment';
|
||||
}
|
||||
|
||||
// Handle number literals
|
||||
if (stream.match(/^-?[0-9\.]/, false)) {
|
||||
var floatLiteral = false;
|
||||
// Floats
|
||||
if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)) {
|
||||
floatLiteral = true;
|
||||
}
|
||||
if (stream.match(/^-?\d+\.\d*/)) {
|
||||
floatLiteral = true;
|
||||
}
|
||||
if (stream.match(/^-?\.\d+/)) {
|
||||
floatLiteral = true;
|
||||
}
|
||||
|
||||
if (floatLiteral) {
|
||||
// prevent from getting extra . on 1..
|
||||
if (stream.peek() == "."){
|
||||
stream.backUp(1);
|
||||
}
|
||||
return 'number';
|
||||
}
|
||||
// Integers
|
||||
var intLiteral = false;
|
||||
// Hex
|
||||
if (stream.match(/^-?0x[0-9a-f]+/i)) {
|
||||
intLiteral = true;
|
||||
}
|
||||
// Decimal
|
||||
if (stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)) {
|
||||
intLiteral = true;
|
||||
}
|
||||
// Zero by itself with no other piece of number.
|
||||
if (stream.match(/^-?0(?![\dx])/i)) {
|
||||
intLiteral = true;
|
||||
}
|
||||
if (intLiteral) {
|
||||
return 'number';
|
||||
}
|
||||
}
|
||||
|
||||
// Handle strings
|
||||
if (stream.match(stringPrefixes)) {
|
||||
state.tokenize = tokenFactory(stream.current(), 'string');
|
||||
return state.tokenize(stream, state);
|
||||
}
|
||||
// Handle regex literals
|
||||
if (stream.match(regexPrefixes)) {
|
||||
if (stream.current() != '/' || stream.match(/^.*\//, false)) { // prevent highlight of division
|
||||
state.tokenize = tokenFactory(stream.current(), 'string-2');
|
||||
return state.tokenize(stream, state);
|
||||
} else {
|
||||
stream.backUp(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle operators and delimiters
|
||||
if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) {
|
||||
return 'punctuation';
|
||||
}
|
||||
if (stream.match(doubleOperators)
|
||||
|| stream.match(singleOperators)
|
||||
|| stream.match(wordOperators)) {
|
||||
return 'operator';
|
||||
}
|
||||
if (stream.match(singleDelimiters)) {
|
||||
return 'punctuation';
|
||||
}
|
||||
|
||||
if (stream.match(constants)) {
|
||||
return 'atom';
|
||||
}
|
||||
|
||||
if (stream.match(keywords)) {
|
||||
return 'keyword';
|
||||
}
|
||||
|
||||
if (stream.match(identifiers)) {
|
||||
return 'variable';
|
||||
}
|
||||
|
||||
// Handle non-detected items
|
||||
stream.next();
|
||||
return ERRORCLASS;
|
||||
}
|
||||
|
||||
function tokenFactory(delimiter, outclass) {
|
||||
var singleline = delimiter.length == 1;
|
||||
return function tokenString(stream, state) {
|
||||
while (!stream.eol()) {
|
||||
stream.eatWhile(/[^'"\/\\]/);
|
||||
if (stream.eat('\\')) {
|
||||
stream.next();
|
||||
if (singleline && stream.eol()) {
|
||||
return outclass;
|
||||
}
|
||||
} else if (stream.match(delimiter)) {
|
||||
state.tokenize = tokenBase;
|
||||
return outclass;
|
||||
} else {
|
||||
stream.eat(/['"\/]/);
|
||||
}
|
||||
}
|
||||
if (singleline) {
|
||||
if (conf.mode.singleLineStringErrors) {
|
||||
outclass = ERRORCLASS
|
||||
} else {
|
||||
state.tokenize = tokenBase;
|
||||
}
|
||||
}
|
||||
return outclass;
|
||||
};
|
||||
}
|
||||
|
||||
function longComment(stream, state) {
|
||||
while (!stream.eol()) {
|
||||
stream.eatWhile(/[^#]/);
|
||||
if (stream.match("###")) {
|
||||
state.tokenize = tokenBase;
|
||||
break;
|
||||
}
|
||||
stream.eatWhile("#");
|
||||
}
|
||||
return "comment"
|
||||
}
|
||||
|
||||
function indent(stream, state, type) {
|
||||
type = type || 'coffee';
|
||||
var indentUnit = 0;
|
||||
if (type === 'coffee') {
|
||||
for (var i = 0; i < state.scopes.length; i++) {
|
||||
if (state.scopes[i].type === 'coffee') {
|
||||
indentUnit = state.scopes[i].offset + conf.indentUnit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
indentUnit = stream.column() + stream.current().length;
|
||||
}
|
||||
state.scopes.unshift({
|
||||
offset: indentUnit,
|
||||
type: type
|
||||
});
|
||||
}
|
||||
|
||||
function dedent(stream, state) {
|
||||
if (state.scopes.length == 1) return;
|
||||
if (state.scopes[0].type === 'coffee') {
|
||||
var _indent = stream.indentation();
|
||||
var _indent_index = -1;
|
||||
for (var i = 0; i < state.scopes.length; ++i) {
|
||||
if (_indent === state.scopes[i].offset) {
|
||||
_indent_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_indent_index === -1) {
|
||||
return true;
|
||||
}
|
||||
while (state.scopes[0].offset !== _indent) {
|
||||
state.scopes.shift();
|
||||
}
|
||||
return false
|
||||
} else {
|
||||
state.scopes.shift();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function tokenLexer(stream, state) {
|
||||
var style = state.tokenize(stream, state);
|
||||
var current = stream.current();
|
||||
|
||||
// Handle '.' connected identifiers
|
||||
if (current === '.') {
|
||||
style = state.tokenize(stream, state);
|
||||
current = stream.current();
|
||||
if (style === 'variable') {
|
||||
return 'variable';
|
||||
} else {
|
||||
return ERRORCLASS;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle properties
|
||||
if (current === '@') {
|
||||
stream.eat('@');
|
||||
return 'keyword';
|
||||
}
|
||||
|
||||
// Handle scope changes.
|
||||
if (current === 'return') {
|
||||
state.dedent += 1;
|
||||
}
|
||||
if (((current === '->' || current === '=>') &&
|
||||
!state.lambda &&
|
||||
state.scopes[0].type == 'coffee' &&
|
||||
stream.peek() === '')
|
||||
|| style === 'indent') {
|
||||
indent(stream, state);
|
||||
}
|
||||
var delimiter_index = '[({'.indexOf(current);
|
||||
if (delimiter_index !== -1) {
|
||||
indent(stream, state, '])}'.slice(delimiter_index, delimiter_index+1));
|
||||
}
|
||||
if (indentKeywords.exec(current)){
|
||||
indent(stream, state);
|
||||
}
|
||||
if (current == 'then'){
|
||||
dedent(stream, state);
|
||||
}
|
||||
|
||||
|
||||
if (style === 'dedent') {
|
||||
if (dedent(stream, state)) {
|
||||
return ERRORCLASS;
|
||||
}
|
||||
}
|
||||
delimiter_index = '])}'.indexOf(current);
|
||||
if (delimiter_index !== -1) {
|
||||
if (dedent(stream, state)) {
|
||||
return ERRORCLASS;
|
||||
}
|
||||
}
|
||||
if (state.dedent > 0 && stream.eol() && state.scopes[0].type == 'coffee') {
|
||||
if (state.scopes.length > 1) state.scopes.shift();
|
||||
state.dedent -= 1;
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
var external = {
|
||||
startState: function(basecolumn) {
|
||||
return {
|
||||
tokenize: tokenBase,
|
||||
scopes: [{offset:basecolumn || 0, type:'coffee'}],
|
||||
lastToken: null,
|
||||
lambda: false,
|
||||
dedent: 0
|
||||
};
|
||||
},
|
||||
|
||||
token: function(stream, state) {
|
||||
var style = tokenLexer(stream, state);
|
||||
|
||||
state.lastToken = {style:style, content: stream.current()};
|
||||
|
||||
if (stream.eol() && stream.lambda) {
|
||||
state.lambda = false;
|
||||
}
|
||||
|
||||
return style;
|
||||
},
|
||||
|
||||
indent: function(state, textAfter) {
|
||||
if (state.tokenize != tokenBase) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return state.scopes[0].offset;
|
||||
}
|
||||
|
||||
};
|
||||
return external;
|
||||
});
|
||||
|
||||
CodeMirror.defineMIME('text/x-coffeescript', 'coffeescript');
|
||||
721
src/flask_debugtoolbar/static/codemirror/mode/coffeescript/index.html
vendored
Normal file
@@ -0,0 +1,721 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CodeMirror: CoffeeScript mode</title>
|
||||
<link rel="stylesheet" href="../../lib/codemirror.css">
|
||||
<script src="../../lib/codemirror.js"></script>
|
||||
<script src="coffeescript.js"></script>
|
||||
<style>.CodeMirror {border-top: 1px solid silver; border-bottom: 1px solid silver;}</style>
|
||||
<link rel="stylesheet" href="../../doc/docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>CodeMirror: CoffeeScript mode</h1>
|
||||
<form><textarea id="code" name="code">
|
||||
# CoffeeScript mode for CodeMirror
|
||||
# Copyright (c) 2011 Jeff Pickhardt, released under
|
||||
# the MIT License.
|
||||
#
|
||||
# Modified from the Python CodeMirror mode, which also is
|
||||
# under the MIT License Copyright (c) 2010 Timothy Farrell.
|
||||
#
|
||||
# The following script, Underscore.coffee, is used to
|
||||
# demonstrate CoffeeScript mode for CodeMirror.
|
||||
#
|
||||
# To download CoffeeScript mode for CodeMirror, go to:
|
||||
# https://github.com/pickhardt/coffeescript-codemirror-mode
|
||||
|
||||
# **Underscore.coffee
|
||||
# (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.**
|
||||
# Underscore is freely distributable under the terms of the
|
||||
# [MIT license](http://en.wikipedia.org/wiki/MIT_License).
|
||||
# Portions of Underscore are inspired by or borrowed from
|
||||
# [Prototype.js](http://prototypejs.org/api), Oliver Steele's
|
||||
# [Functional](http://osteele.com), and John Resig's
|
||||
# [Micro-Templating](http://ejohn.org).
|
||||
# For all details and documentation:
|
||||
# http://documentcloud.github.com/underscore/
|
||||
|
||||
|
||||
# Baseline setup
|
||||
# --------------
|
||||
|
||||
# Establish the root object, `window` in the browser, or `global` on the server.
|
||||
root = this
|
||||
|
||||
|
||||
# Save the previous value of the `_` variable.
|
||||
previousUnderscore = root._
|
||||
|
||||
|
||||
# Establish the object that gets thrown to break out of a loop iteration.
|
||||
# `StopIteration` is SOP on Mozilla.
|
||||
breaker = if typeof(StopIteration) is 'undefined' then '__break__' else StopIteration
|
||||
|
||||
|
||||
# Helper function to escape **RegExp** contents, because JS doesn't have one.
|
||||
escapeRegExp = (string) -> string.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1')
|
||||
|
||||
|
||||
# Save bytes in the minified (but not gzipped) version:
|
||||
ArrayProto = Array.prototype
|
||||
ObjProto = Object.prototype
|
||||
|
||||
|
||||
# Create quick reference variables for speed access to core prototypes.
|
||||
slice = ArrayProto.slice
|
||||
unshift = ArrayProto.unshift
|
||||
toString = ObjProto.toString
|
||||
hasOwnProperty = ObjProto.hasOwnProperty
|
||||
propertyIsEnumerable = ObjProto.propertyIsEnumerable
|
||||
|
||||
|
||||
# All **ECMA5** native implementations we hope to use are declared here.
|
||||
nativeForEach = ArrayProto.forEach
|
||||
nativeMap = ArrayProto.map
|
||||
nativeReduce = ArrayProto.reduce
|
||||
nativeReduceRight = ArrayProto.reduceRight
|
||||
nativeFilter = ArrayProto.filter
|
||||
nativeEvery = ArrayProto.every
|
||||
nativeSome = ArrayProto.some
|
||||
nativeIndexOf = ArrayProto.indexOf
|
||||
nativeLastIndexOf = ArrayProto.lastIndexOf
|
||||
nativeIsArray = Array.isArray
|
||||
nativeKeys = Object.keys
|
||||
|
||||
|
||||
# Create a safe reference to the Underscore object for use below.
|
||||
_ = (obj) -> new wrapper(obj)
|
||||
|
||||
|
||||
# Export the Underscore object for **CommonJS**.
|
||||
if typeof(exports) != 'undefined' then exports._ = _
|
||||
|
||||
|
||||
# Export Underscore to global scope.
|
||||
root._ = _
|
||||
|
||||
|
||||
# Current version.
|
||||
_.VERSION = '1.1.0'
|
||||
|
||||
|
||||
# Collection Functions
|
||||
# --------------------
|
||||
|
||||
# The cornerstone, an **each** implementation.
|
||||
# Handles objects implementing **forEach**, arrays, and raw objects.
|
||||
_.each = (obj, iterator, context) ->
|
||||
try
|
||||
if nativeForEach and obj.forEach is nativeForEach
|
||||
obj.forEach iterator, context
|
||||
else if _.isNumber obj.length
|
||||
iterator.call context, obj[i], i, obj for i in [0...obj.length]
|
||||
else
|
||||
iterator.call context, val, key, obj for own key, val of obj
|
||||
catch e
|
||||
throw e if e isnt breaker
|
||||
obj
|
||||
|
||||
|
||||
# Return the results of applying the iterator to each element. Use JavaScript
|
||||
# 1.6's version of **map**, if possible.
|
||||
_.map = (obj, iterator, context) ->
|
||||
return obj.map(iterator, context) if nativeMap and obj.map is nativeMap
|
||||
results = []
|
||||
_.each obj, (value, index, list) ->
|
||||
results.push iterator.call context, value, index, list
|
||||
results
|
||||
|
||||
|
||||
# **Reduce** builds up a single result from a list of values. Also known as
|
||||
# **inject**, or **foldl**. Uses JavaScript 1.8's version of **reduce**, if possible.
|
||||
_.reduce = (obj, iterator, memo, context) ->
|
||||
if nativeReduce and obj.reduce is nativeReduce
|
||||
iterator = _.bind iterator, context if context
|
||||
return obj.reduce iterator, memo
|
||||
_.each obj, (value, index, list) ->
|
||||
memo = iterator.call context, memo, value, index, list
|
||||
memo
|
||||
|
||||
|
||||
# The right-associative version of **reduce**, also known as **foldr**. Uses
|
||||
# JavaScript 1.8's version of **reduceRight**, if available.
|
||||
_.reduceRight = (obj, iterator, memo, context) ->
|
||||
if nativeReduceRight and obj.reduceRight is nativeReduceRight
|
||||
iterator = _.bind iterator, context if context
|
||||
return obj.reduceRight iterator, memo
|
||||
reversed = _.clone(_.toArray(obj)).reverse()
|
||||
_.reduce reversed, iterator, memo, context
|
||||
|
||||
|
||||
# Return the first value which passes a truth test.
|
||||
_.detect = (obj, iterator, context) ->
|
||||
result = null
|
||||
_.each obj, (value, index, list) ->
|
||||
if iterator.call context, value, index, list
|
||||
result = value
|
||||
_.breakLoop()
|
||||
result
|
||||
|
||||
|
||||
# Return all the elements that pass a truth test. Use JavaScript 1.6's
|
||||
# **filter**, if it exists.
|
||||
_.filter = (obj, iterator, context) ->
|
||||
return obj.filter iterator, context if nativeFilter and obj.filter is nativeFilter
|
||||
results = []
|
||||
_.each obj, (value, index, list) ->
|
||||
results.push value if iterator.call context, value, index, list
|
||||
results
|
||||
|
||||
|
||||
# Return all the elements for which a truth test fails.
|
||||
_.reject = (obj, iterator, context) ->
|
||||
results = []
|
||||
_.each obj, (value, index, list) ->
|
||||
results.push value if not iterator.call context, value, index, list
|
||||
results
|
||||
|
||||
|
||||
# Determine whether all of the elements match a truth test. Delegate to
|
||||
# JavaScript 1.6's **every**, if it is present.
|
||||
_.every = (obj, iterator, context) ->
|
||||
iterator ||= _.identity
|
||||
return obj.every iterator, context if nativeEvery and obj.every is nativeEvery
|
||||
result = true
|
||||
_.each obj, (value, index, list) ->
|
||||
_.breakLoop() unless (result = result and iterator.call(context, value, index, list))
|
||||
result
|
||||
|
||||
|
||||
# Determine if at least one element in the object matches a truth test. Use
|
||||
# JavaScript 1.6's **some**, if it exists.
|
||||
_.some = (obj, iterator, context) ->
|
||||
iterator ||= _.identity
|
||||
return obj.some iterator, context if nativeSome and obj.some is nativeSome
|
||||
result = false
|
||||
_.each obj, (value, index, list) ->
|
||||
_.breakLoop() if (result = iterator.call(context, value, index, list))
|
||||
result
|
||||
|
||||
|
||||
# Determine if a given value is included in the array or object,
|
||||
# based on `===`.
|
||||
_.include = (obj, target) ->
|
||||
return _.indexOf(obj, target) isnt -1 if nativeIndexOf and obj.indexOf is nativeIndexOf
|
||||
return true for own key, val of obj when val is target
|
||||
false
|
||||
|
||||
|
||||
# Invoke a method with arguments on every item in a collection.
|
||||
_.invoke = (obj, method) ->
|
||||
args = _.rest arguments, 2
|
||||
(if method then val[method] else val).apply(val, args) for val in obj
|
||||
|
||||
|
||||
# Convenience version of a common use case of **map**: fetching a property.
|
||||
_.pluck = (obj, key) ->
|
||||
_.map(obj, (val) -> val[key])
|
||||
|
||||
|
||||
# Return the maximum item or (item-based computation).
|
||||
_.max = (obj, iterator, context) ->
|
||||
return Math.max.apply(Math, obj) if not iterator and _.isArray(obj)
|
||||
result = computed: -Infinity
|
||||
_.each obj, (value, index, list) ->
|
||||
computed = if iterator then iterator.call(context, value, index, list) else value
|
||||
computed >= result.computed and (result = {value: value, computed: computed})
|
||||
result.value
|
||||
|
||||
|
||||
# Return the minimum element (or element-based computation).
|
||||
_.min = (obj, iterator, context) ->
|
||||
return Math.min.apply(Math, obj) if not iterator and _.isArray(obj)
|
||||
result = computed: Infinity
|
||||
_.each obj, (value, index, list) ->
|
||||
computed = if iterator then iterator.call(context, value, index, list) else value
|
||||
computed < result.computed and (result = {value: value, computed: computed})
|
||||
result.value
|
||||
|
||||
|
||||
# Sort the object's values by a criterion produced by an iterator.
|
||||
_.sortBy = (obj, iterator, context) ->
|
||||
_.pluck(((_.map obj, (value, index, list) ->
|
||||
{value: value, criteria: iterator.call(context, value, index, list)}
|
||||
).sort((left, right) ->
|
||||
a = left.criteria; b = right.criteria
|
||||
if a < b then -1 else if a > b then 1 else 0
|
||||
)), 'value')
|
||||
|
||||
|
||||
# Use a comparator function to figure out at what index an object should
|
||||
# be inserted so as to maintain order. Uses binary search.
|
||||
_.sortedIndex = (array, obj, iterator) ->
|
||||
iterator ||= _.identity
|
||||
low = 0
|
||||
high = array.length
|
||||
while low < high
|
||||
mid = (low + high) >> 1
|
||||
if iterator(array[mid]) < iterator(obj) then low = mid + 1 else high = mid
|
||||
low
|
||||
|
||||
|
||||
# Convert anything iterable into a real, live array.
|
||||
_.toArray = (iterable) ->
|
||||
return [] if (!iterable)
|
||||
return iterable.toArray() if (iterable.toArray)
|
||||
return iterable if (_.isArray(iterable))
|
||||
return slice.call(iterable) if (_.isArguments(iterable))
|
||||
_.values(iterable)
|
||||
|
||||
|
||||
# Return the number of elements in an object.
|
||||
_.size = (obj) -> _.toArray(obj).length
|
||||
|
||||
|
||||
# Array Functions
|
||||
# ---------------
|
||||
|
||||
# Get the first element of an array. Passing `n` will return the first N
|
||||
# values in the array. Aliased as **head**. The `guard` check allows it to work
|
||||
# with **map**.
|
||||
_.first = (array, n, guard) ->
|
||||
if n and not guard then slice.call(array, 0, n) else array[0]
|
||||
|
||||
|
||||
# Returns everything but the first entry of the array. Aliased as **tail**.
|
||||
# Especially useful on the arguments object. Passing an `index` will return
|
||||
# the rest of the values in the array from that index onward. The `guard`
|
||||
# check allows it to work with **map**.
|
||||
_.rest = (array, index, guard) ->
|
||||
slice.call(array, if _.isUndefined(index) or guard then 1 else index)
|
||||
|
||||
|
||||
# Get the last element of an array.
|
||||
_.last = (array) -> array[array.length - 1]
|
||||
|
||||
|
||||
# Trim out all falsy values from an array.
|
||||
_.compact = (array) -> item for item in array when item
|
||||
|
||||
|
||||
# Return a completely flattened version of an array.
|
||||
_.flatten = (array) ->
|
||||
_.reduce array, (memo, value) ->
|
||||
return memo.concat(_.flatten(value)) if _.isArray value
|
||||
memo.push value
|
||||
memo
|
||||
, []
|
||||
|
||||
|
||||
# Return a version of the array that does not contain the specified value(s).
|
||||
_.without = (array) ->
|
||||
values = _.rest arguments
|
||||
val for val in _.toArray(array) when not _.include values, val
|
||||
|
||||
|
||||
# Produce a duplicate-free version of the array. If the array has already
|
||||
# been sorted, you have the option of using a faster algorithm.
|
||||
_.uniq = (array, isSorted) ->
|
||||
memo = []
|
||||
for el, i in _.toArray array
|
||||
memo.push el if i is 0 || (if isSorted is true then _.last(memo) isnt el else not _.include(memo, el))
|
||||
memo
|
||||
|
||||
|
||||
# Produce an array that contains every item shared between all the
|
||||
# passed-in arrays.
|
||||
_.intersect = (array) ->
|
||||
rest = _.rest arguments
|
||||
_.select _.uniq(array), (item) ->
|
||||
_.all rest, (other) ->
|
||||
_.indexOf(other, item) >= 0
|
||||
|
||||
|
||||
# Zip together multiple lists into a single array -- elements that share
|
||||
# an index go together.
|
||||
_.zip = ->
|
||||
length = _.max _.pluck arguments, 'length'
|
||||
results = new Array length
|
||||
for i in [0...length]
|
||||
results[i] = _.pluck arguments, String i
|
||||
results
|
||||
|
||||
|
||||
# If the browser doesn't supply us with **indexOf** (I'm looking at you, MSIE),
|
||||
# we need this function. Return the position of the first occurrence of an
|
||||
# item in an array, or -1 if the item is not included in the array.
|
||||
_.indexOf = (array, item) ->
|
||||
return array.indexOf item if nativeIndexOf and array.indexOf is nativeIndexOf
|
||||
i = 0; l = array.length
|
||||
while l - i
|
||||
if array[i] is item then return i else i++
|
||||
-1
|
||||
|
||||
|
||||
# Provide JavaScript 1.6's **lastIndexOf**, delegating to the native function,
|
||||
# if possible.
|
||||
_.lastIndexOf = (array, item) ->
|
||||
return array.lastIndexOf(item) if nativeLastIndexOf and array.lastIndexOf is nativeLastIndexOf
|
||||
i = array.length
|
||||
while i
|
||||
if array[i] is item then return i else i--
|
||||
-1
|
||||
|
||||
|
||||
# Generate an integer Array containing an arithmetic progression. A port of
|
||||
# [the native Python **range** function](http://docs.python.org/library/functions.html#range).
|
||||
_.range = (start, stop, step) ->
|
||||
a = arguments
|
||||
solo = a.length <= 1
|
||||
i = start = if solo then 0 else a[0]
|
||||
stop = if solo then a[0] else a[1]
|
||||
step = a[2] or 1
|
||||
len = Math.ceil((stop - start) / step)
|
||||
return [] if len <= 0
|
||||
range = new Array len
|
||||
idx = 0
|
||||
loop
|
||||
return range if (if step > 0 then i - stop else stop - i) >= 0
|
||||
range[idx] = i
|
||||
idx++
|
||||
i+= step
|
||||
|
||||
|
||||
# Function Functions
|
||||
# ------------------
|
||||
|
||||
# Create a function bound to a given object (assigning `this`, and arguments,
|
||||
# optionally). Binding with arguments is also known as **curry**.
|
||||
_.bind = (func, obj) ->
|
||||
args = _.rest arguments, 2
|
||||
-> func.apply obj or root, args.concat arguments
|
||||
|
||||
|
||||
# Bind all of an object's methods to that object. Useful for ensuring that
|
||||
# all callbacks defined on an object belong to it.
|
||||
_.bindAll = (obj) ->
|
||||
funcs = if arguments.length > 1 then _.rest(arguments) else _.functions(obj)
|
||||
_.each funcs, (f) -> obj[f] = _.bind obj[f], obj
|
||||
obj
|
||||
|
||||
|
||||
# Delays a function for the given number of milliseconds, and then calls
|
||||
# it with the arguments supplied.
|
||||
_.delay = (func, wait) ->
|
||||
args = _.rest arguments, 2
|
||||
setTimeout((-> func.apply(func, args)), wait)
|
||||
|
||||
|
||||
# Memoize an expensive function by storing its results.
|
||||
_.memoize = (func, hasher) ->
|
||||
memo = {}
|
||||
hasher or= _.identity
|
||||
->
|
||||
key = hasher.apply this, arguments
|
||||
return memo[key] if key of memo
|
||||
memo[key] = func.apply this, arguments
|
||||
|
||||
|
||||
# Defers a function, scheduling it to run after the current call stack has
|
||||
# cleared.
|
||||
_.defer = (func) ->
|
||||
_.delay.apply _, [func, 1].concat _.rest arguments
|
||||
|
||||
|
||||
# Returns the first function passed as an argument to the second,
|
||||
# allowing you to adjust arguments, run code before and after, and
|
||||
# conditionally execute the original function.
|
||||
_.wrap = (func, wrapper) ->
|
||||
-> wrapper.apply wrapper, [func].concat arguments
|
||||
|
||||
|
||||
# Returns a function that is the composition of a list of functions, each
|
||||
# consuming the return value of the function that follows.
|
||||
_.compose = ->
|
||||
funcs = arguments
|
||||
->
|
||||
args = arguments
|
||||
for i in [funcs.length - 1..0] by -1
|
||||
args = [funcs[i].apply(this, args)]
|
||||
args[0]
|
||||
|
||||
|
||||
# Object Functions
|
||||
# ----------------
|
||||
|
||||
# Retrieve the names of an object's properties.
|
||||
_.keys = nativeKeys or (obj) ->
|
||||
return _.range 0, obj.length if _.isArray(obj)
|
||||
key for key, val of obj
|
||||
|
||||
|
||||
# Retrieve the values of an object's properties.
|
||||
_.values = (obj) ->
|
||||
_.map obj, _.identity
|
||||
|
||||
|
||||
# Return a sorted list of the function names available in Underscore.
|
||||
_.functions = (obj) ->
|
||||
_.filter(_.keys(obj), (key) -> _.isFunction(obj[key])).sort()
|
||||
|
||||
|
||||
# Extend a given object with all of the properties in a source object.
|
||||
_.extend = (obj) ->
|
||||
for source in _.rest(arguments)
|
||||
obj[key] = val for key, val of source
|
||||
obj
|
||||
|
||||
|
||||
# Create a (shallow-cloned) duplicate of an object.
|
||||
_.clone = (obj) ->
|
||||
return obj.slice 0 if _.isArray obj
|
||||
_.extend {}, obj
|
||||
|
||||
|
||||
# Invokes interceptor with the obj, and then returns obj.
|
||||
# The primary purpose of this method is to "tap into" a method chain,
|
||||
# in order to perform operations on intermediate results within
|
||||
the chain.
|
||||
_.tap = (obj, interceptor) ->
|
||||
interceptor obj
|
||||
obj
|
||||
|
||||
|
||||
# Perform a deep comparison to check if two objects are equal.
|
||||
_.isEqual = (a, b) ->
|
||||
# Check object identity.
|
||||
return true if a is b
|
||||
# Different types?
|
||||
atype = typeof(a); btype = typeof(b)
|
||||
return false if atype isnt btype
|
||||
# Basic equality test (watch out for coercions).
|
||||
return true if `a == b`
|
||||
# One is falsy and the other truthy.
|
||||
return false if (!a and b) or (a and !b)
|
||||
# One of them implements an `isEqual()`?
|
||||
return a.isEqual(b) if a.isEqual
|
||||
# Check dates' integer values.
|
||||
return a.getTime() is b.getTime() if _.isDate(a) and _.isDate(b)
|
||||
# Both are NaN?
|
||||
return false if _.isNaN(a) and _.isNaN(b)
|
||||
# Compare regular expressions.
|
||||
if _.isRegExp(a) and _.isRegExp(b)
|
||||
return a.source is b.source and
|
||||
a.global is b.global and
|
||||
a.ignoreCase is b.ignoreCase and
|
||||
a.multiline is b.multiline
|
||||
# If a is not an object by this point, we can't handle it.
|
||||
return false if atype isnt 'object'
|
||||
# Check for different array lengths before comparing contents.
|
||||
return false if a.length and (a.length isnt b.length)
|
||||
# Nothing else worked, deep compare the contents.
|
||||
aKeys = _.keys(a); bKeys = _.keys(b)
|
||||
# Different object sizes?
|
||||
return false if aKeys.length isnt bKeys.length
|
||||
# Recursive comparison of contents.
|
||||
return false for key, val of a when !(key of b) or !_.isEqual(val, b[key])
|
||||
true
|
||||
|
||||
|
||||
# Is a given array or object empty?
|
||||
_.isEmpty = (obj) ->
|
||||
return obj.length is 0 if _.isArray(obj) or _.isString(obj)
|
||||
return false for own key of obj
|
||||
true
|
||||
|
||||
|
||||
# Is a given value a DOM element?
|
||||
_.isElement = (obj) -> obj and obj.nodeType is 1
|
||||
|
||||
|
||||
# Is a given value an array?
|
||||
_.isArray = nativeIsArray or (obj) -> !!(obj and obj.concat and obj.unshift and not obj.callee)
|
||||
|
||||
|
||||
# Is a given variable an arguments object?
|
||||
_.isArguments = (obj) -> obj and obj.callee
|
||||
|
||||
|
||||
# Is the given value a function?
|
||||
_.isFunction = (obj) -> !!(obj and obj.constructor and obj.call and obj.apply)
|
||||
|
||||
|
||||
# Is the given value a string?
|
||||
_.isString = (obj) -> !!(obj is '' or (obj and obj.charCodeAt and obj.substr))
|
||||
|
||||
|
||||
# Is a given value a number?
|
||||
_.isNumber = (obj) -> (obj is +obj) or toString.call(obj) is '[object Number]'
|
||||
|
||||
|
||||
# Is a given value a boolean?
|
||||
_.isBoolean = (obj) -> obj is true or obj is false
|
||||
|
||||
|
||||
# Is a given value a Date?
|
||||
_.isDate = (obj) -> !!(obj and obj.getTimezoneOffset and obj.setUTCFullYear)
|
||||
|
||||
|
||||
# Is the given value a regular expression?
|
||||
_.isRegExp = (obj) -> !!(obj and obj.exec and (obj.ignoreCase or obj.ignoreCase is false))
|
||||
|
||||
|
||||
# Is the given value NaN -- this one is interesting. `NaN != NaN`, and
|
||||
# `isNaN(undefined) == true`, so we make sure it's a number first.
|
||||
_.isNaN = (obj) -> _.isNumber(obj) and window.isNaN(obj)
|
||||
|
||||
|
||||
# Is a given value equal to null?
|
||||
_.isNull = (obj) -> obj is null
|
||||
|
||||
|
||||
# Is a given variable undefined?
|
||||
_.isUndefined = (obj) -> typeof obj is 'undefined'
|
||||
|
||||
|
||||
# Utility Functions
|
||||
# -----------------
|
||||
|
||||
# Run Underscore.js in noConflict mode, returning the `_` variable to its
|
||||
# previous owner. Returns a reference to the Underscore object.
|
||||
_.noConflict = ->
|
||||
root._ = previousUnderscore
|
||||
this
|
||||
|
||||
|
||||
# Keep the identity function around for default iterators.
|
||||
_.identity = (value) -> value
|
||||
|
||||
|
||||
# Run a function `n` times.
|
||||
_.times = (n, iterator, context) ->
|
||||
iterator.call context, i for i in [0...n]
|
||||
|
||||
|
||||
# Break out of the middle of an iteration.
|
||||
_.breakLoop = -> throw breaker
|
||||
|
||||
|
||||
# Add your own custom functions to the Underscore object, ensuring that
|
||||
# they're correctly added to the OOP wrapper as well.
|
||||
_.mixin = (obj) ->
|
||||
for name in _.functions(obj)
|
||||
addToWrapper name, _[name] = obj[name]
|
||||
|
||||
|
||||
# Generate a unique integer id (unique within the entire client session).
|
||||
# Useful for temporary DOM ids.
|
||||
idCounter = 0
|
||||
_.uniqueId = (prefix) ->
|
||||
(prefix or '') + idCounter++
|
||||
|
||||
|
||||
# By default, Underscore uses **ERB**-style template delimiters, change the
|
||||
# following template settings to use alternative delimiters.
|
||||
_.templateSettings = {
|
||||
start: '<%'
|
||||
end: '%>'
|
||||
interpolate: /<%=(.+?)%>/g
|
||||
}
|
||||
|
||||
|
||||
# JavaScript templating a-la **ERB**, pilfered from John Resig's
|
||||
# *Secrets of the JavaScript Ninja*, page 83.
|
||||
# Single-quote fix from Rick Strahl.
|
||||
# With alterations for arbitrary delimiters, and to preserve whitespace.
|
||||
_.template = (str, data) ->
|
||||
c = _.templateSettings
|
||||
endMatch = new RegExp("'(?=[^"+c.end.substr(0, 1)+"]*"+escapeRegExp(c.end)+")","g")
|
||||
fn = new Function 'obj',
|
||||
'var p=[],print=function(){p.push.apply(p,arguments);};' +
|
||||
'with(obj||{}){p.push(\'' +
|
||||
str.replace(/\r/g, '\\r')
|
||||
.replace(/\n/g, '\\n')
|
||||
.replace(/\t/g, '\\t')
|
||||
.replace(endMatch,"<22><><EFBFBD>")
|
||||
.split("'").join("\\'")
|
||||
.split("<22><><EFBFBD>").join("'")
|
||||
.replace(c.interpolate, "',$1,'")
|
||||
.split(c.start).join("');")
|
||||
.split(c.end).join("p.push('") +
|
||||
"');}return p.join('');"
|
||||
if data then fn(data) else fn
|
||||
|
||||
|
||||
# Aliases
|
||||
# -------
|
||||
|
||||
_.forEach = _.each
|
||||
_.foldl = _.inject = _.reduce
|
||||
_.foldr = _.reduceRight
|
||||
_.select = _.filter
|
||||
_.all = _.every
|
||||
_.any = _.some
|
||||
_.contains = _.include
|
||||
_.head = _.first
|
||||
_.tail = _.rest
|
||||
_.methods = _.functions
|
||||
|
||||
|
||||
# Setup the OOP Wrapper
|
||||
# ---------------------
|
||||
|
||||
# If Underscore is called as a function, it returns a wrapped object that
|
||||
# can be used OO-style. This wrapper holds altered versions of all the
|
||||
# underscore functions. Wrapped objects may be chained.
|
||||
wrapper = (obj) ->
|
||||
this._wrapped = obj
|
||||
this
|
||||
|
||||
|
||||
# Helper function to continue chaining intermediate results.
|
||||
result = (obj, chain) ->
|
||||
if chain then _(obj).chain() else obj
|
||||
|
||||
|
||||
# A method to easily add functions to the OOP wrapper.
|
||||
addToWrapper = (name, func) ->
|
||||
wrapper.prototype[name] = ->
|
||||
args = _.toArray arguments
|
||||
unshift.call args, this._wrapped
|
||||
result func.apply(_, args), this._chain
|
||||
|
||||
|
||||
# Add all ofthe Underscore functions to the wrapper object.
|
||||
_.mixin _
|
||||
|
||||
|
||||
# Add all mutator Array functions to the wrapper.
|
||||
_.each ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], (name) ->
|
||||
method = Array.prototype[name]
|
||||
wrapper.prototype[name] = ->
|
||||
method.apply(this._wrapped, arguments)
|
||||
result(this._wrapped, this._chain)
|
||||
|
||||
|
||||
# Add all accessor Array functions to the wrapper.
|
||||
_.each ['concat', 'join', 'slice'], (name) ->
|
||||
method = Array.prototype[name]
|
||||
wrapper.prototype[name] = ->
|
||||
result(method.apply(this._wrapped, arguments), this._chain)
|
||||
|
||||
|
||||
# Start chaining a wrapped Underscore object.
|
||||
wrapper::chain = ->
|
||||
this._chain = true
|
||||
this
|
||||
|
||||
|
||||
# Extracts the result from a wrapped and chained object.
|
||||
wrapper::value = -> this._wrapped
|
||||
</textarea></form>
|
||||
<script>
|
||||
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {});
|
||||
</script>
|
||||
|
||||
<p><strong>MIME types defined:</strong> <code>text/x-coffeescript</code>.</p>
|
||||
|
||||
<p>The CoffeeScript mode was written by Jeff Pickhardt (<a href="LICENSE">license</a>).</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
124
src/flask_debugtoolbar/static/codemirror/mode/css/css.js
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
CodeMirror.defineMode("css", function(config) {
|
||||
var indentUnit = config.indentUnit, type;
|
||||
function ret(style, tp) {type = tp; return style;}
|
||||
|
||||
function tokenBase(stream, state) {
|
||||
var ch = stream.next();
|
||||
if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("meta", stream.current());}
|
||||
else if (ch == "/" && stream.eat("*")) {
|
||||
state.tokenize = tokenCComment;
|
||||
return tokenCComment(stream, state);
|
||||
}
|
||||
else if (ch == "<" && stream.eat("!")) {
|
||||
state.tokenize = tokenSGMLComment;
|
||||
return tokenSGMLComment(stream, state);
|
||||
}
|
||||
else if (ch == "=") ret(null, "compare");
|
||||
else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare");
|
||||
else if (ch == "\"" || ch == "'") {
|
||||
state.tokenize = tokenString(ch);
|
||||
return state.tokenize(stream, state);
|
||||
}
|
||||
else if (ch == "#") {
|
||||
stream.eatWhile(/[\w\\\-]/);
|
||||
return ret("atom", "hash");
|
||||
}
|
||||
else if (ch == "!") {
|
||||
stream.match(/^\s*\w*/);
|
||||
return ret("keyword", "important");
|
||||
}
|
||||
else if (/\d/.test(ch)) {
|
||||
stream.eatWhile(/[\w.%]/);
|
||||
return ret("number", "unit");
|
||||
}
|
||||
else if (/[,.+>*\/]/.test(ch)) {
|
||||
return ret(null, "select-op");
|
||||
}
|
||||
else if (/[;{}:\[\]]/.test(ch)) {
|
||||
return ret(null, ch);
|
||||
}
|
||||
else {
|
||||
stream.eatWhile(/[\w\\\-]/);
|
||||
return ret("variable", "variable");
|
||||
}
|
||||
}
|
||||
|
||||
function tokenCComment(stream, state) {
|
||||
var maybeEnd = false, ch;
|
||||
while ((ch = stream.next()) != null) {
|
||||
if (maybeEnd && ch == "/") {
|
||||
state.tokenize = tokenBase;
|
||||
break;
|
||||
}
|
||||
maybeEnd = (ch == "*");
|
||||
}
|
||||
return ret("comment", "comment");
|
||||
}
|
||||
|
||||
function tokenSGMLComment(stream, state) {
|
||||
var dashes = 0, ch;
|
||||
while ((ch = stream.next()) != null) {
|
||||
if (dashes >= 2 && ch == ">") {
|
||||
state.tokenize = tokenBase;
|
||||
break;
|
||||
}
|
||||
dashes = (ch == "-") ? dashes + 1 : 0;
|
||||
}
|
||||
return ret("comment", "comment");
|
||||
}
|
||||
|
||||
function tokenString(quote) {
|
||||
return function(stream, state) {
|
||||
var escaped = false, ch;
|
||||
while ((ch = stream.next()) != null) {
|
||||
if (ch == quote && !escaped)
|
||||
break;
|
||||
escaped = !escaped && ch == "\\";
|
||||
}
|
||||
if (!escaped) state.tokenize = tokenBase;
|
||||
return ret("string", "string");
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
startState: function(base) {
|
||||
return {tokenize: tokenBase,
|
||||
baseIndent: base || 0,
|
||||
stack: []};
|
||||
},
|
||||
|
||||
token: function(stream, state) {
|
||||
if (stream.eatSpace()) return null;
|
||||
var style = state.tokenize(stream, state);
|
||||
|
||||
var context = state.stack[state.stack.length-1];
|
||||
if (type == "hash" && context != "rule") style = "string-2";
|
||||
else if (style == "variable") {
|
||||
if (context == "rule") style = "number";
|
||||
else if (!context || context == "@media{") style = "tag";
|
||||
}
|
||||
|
||||
if (context == "rule" && /^[\{\};]$/.test(type))
|
||||
state.stack.pop();
|
||||
if (type == "{") {
|
||||
if (context == "@media") state.stack[state.stack.length-1] = "@media{";
|
||||
else state.stack.push("{");
|
||||
}
|
||||
else if (type == "}") state.stack.pop();
|
||||
else if (type == "@media") state.stack.push("@media");
|
||||
else if (context == "{" && type != "comment") state.stack.push("rule");
|
||||
return style;
|
||||
},
|
||||
|
||||
indent: function(state, textAfter) {
|
||||
var n = state.stack.length;
|
||||
if (/^\}/.test(textAfter))
|
||||
n -= state.stack[state.stack.length-1] == "rule" ? 2 : 1;
|
||||
return state.baseIndent + n * indentUnit;
|
||||
},
|
||||
|
||||
electricChars: "}"
|
||||
};
|
||||
});
|
||||
|
||||
CodeMirror.defineMIME("text/css", "css");
|
||||
55
src/flask_debugtoolbar/static/codemirror/mode/css/index.html
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CodeMirror: CSS mode</title>
|
||||
<link rel="stylesheet" href="../../lib/codemirror.css">
|
||||
<script src="../../lib/codemirror.js"></script>
|
||||
<script src="css.js"></script>
|
||||
<style>.CodeMirror {background: #f8f8f8;}</style>
|
||||
<link rel="stylesheet" href="../../doc/docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>CodeMirror: CSS mode</h1>
|
||||
<form><textarea id="code" name="code">
|
||||
/* Some example CSS */
|
||||
|
||||
@import url("something.css");
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 3em 6em;
|
||||
font-family: tahoma, arial, sans-serif;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
#navigation a {
|
||||
font-weight: bold;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.7em;
|
||||
}
|
||||
|
||||
h1:before, h2:before {
|
||||
content: "::";
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: courier, monospace;
|
||||
font-size: 80%;
|
||||
color: #418A8A;
|
||||
}
|
||||
</textarea></form>
|
||||
<script>
|
||||
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {});
|
||||
</script>
|
||||
|
||||
<p><strong>MIME types defined:</strong> <code>text/css</code>.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
3
src/flask_debugtoolbar/static/codemirror/mode/diff/diff.css
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
span.cm-rangeinfo {color: #a0b;}
|
||||
span.cm-minus {color: red;}
|
||||
span.cm-plus {color: #2b2;}
|
||||
13
src/flask_debugtoolbar/static/codemirror/mode/diff/diff.js
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
CodeMirror.defineMode("diff", function() {
|
||||
return {
|
||||
token: function(stream) {
|
||||
var ch = stream.next();
|
||||
stream.skipToEnd();
|
||||
if (ch == "+") return "plus";
|
||||
if (ch == "-") return "minus";
|
||||
if (ch == "@") return "rangeinfo";
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
CodeMirror.defineMIME("text/x-diff", "diff");
|
||||
99
src/flask_debugtoolbar/static/codemirror/mode/diff/index.html
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CodeMirror: Diff mode</title>
|
||||
<link rel="stylesheet" href="../../lib/codemirror.css">
|
||||
<script src="../../lib/codemirror.js"></script>
|
||||
<script src="diff.js"></script>
|
||||
<link rel="stylesheet" href="diff.css">
|
||||
<style>.CodeMirror {border-top: 1px solid #ddd; border-bottom: 1px solid #ddd;}</style>
|
||||
<link rel="stylesheet" href="../../doc/docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>CodeMirror: Diff mode</h1>
|
||||
<form><textarea id="code" name="code">
|
||||
diff --git a/index.html b/index.html
|
||||
index c1d9156..7764744 100644
|
||||
--- a/index.html
|
||||
+++ b/index.html
|
||||
@@ -95,7 +95,8 @@ StringStream.prototype = {
|
||||
<script>
|
||||
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
||||
lineNumbers: true,
|
||||
- autoMatchBrackets: true
|
||||
+ autoMatchBrackets: true,
|
||||
+ onGutterClick: function(x){console.log(x);}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
diff --git a/lib/codemirror.js b/lib/codemirror.js
|
||||
index 04646a9..9a39cc7 100644
|
||||
--- a/lib/codemirror.js
|
||||
+++ b/lib/codemirror.js
|
||||
@@ -399,10 +399,16 @@ var CodeMirror = (function() {
|
||||
}
|
||||
|
||||
function onMouseDown(e) {
|
||||
- var start = posFromMouse(e), last = start;
|
||||
+ var start = posFromMouse(e), last = start, target = e.target();
|
||||
if (!start) return;
|
||||
setCursor(start.line, start.ch, false);
|
||||
if (e.button() != 1) return;
|
||||
+ if (target.parentNode == gutter) {
|
||||
+ if (options.onGutterClick)
|
||||
+ options.onGutterClick(indexOf(gutter.childNodes, target) + showingFrom);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
if (!focused) onFocus();
|
||||
|
||||
e.stop();
|
||||
@@ -808,7 +814,7 @@ var CodeMirror = (function() {
|
||||
for (var i = showingFrom; i < showingTo; ++i) {
|
||||
var marker = lines[i].gutterMarker;
|
||||
if (marker) html.push('<div class="' + marker.style + '">' + htmlEscape(marker.text) + '</div>');
|
||||
- else html.push("<div>" + (options.lineNumbers ? i + 1 : "\u00a0") + "</div>");
|
||||
+ else html.push("<div>" + (options.lineNumbers ? i + options.firstLineNumber : "\u00a0") + "</div>");
|
||||
}
|
||||
gutter.style.display = "none"; // TODO test whether this actually helps
|
||||
gutter.innerHTML = html.join("");
|
||||
@@ -1371,10 +1377,8 @@ var CodeMirror = (function() {
|
||||
if (option == "parser") setParser(value);
|
||||
else if (option === "lineNumbers") setLineNumbers(value);
|
||||
else if (option === "gutter") setGutter(value);
|
||||
- else if (option === "readOnly") options.readOnly = value;
|
||||
- else if (option === "indentUnit") {options.indentUnit = indentUnit = value; setParser(options.parser);}
|
||||
- else if (/^(?:enterMode|tabMode|indentWithTabs|readOnly|autoMatchBrackets|undoDepth)$/.test(option)) options[option] = value;
|
||||
- else throw new Error("Can't set option " + option);
|
||||
+ else if (option === "indentUnit") {options.indentUnit = value; setParser(options.parser);}
|
||||
+ else options[option] = value;
|
||||
},
|
||||
cursorCoords: cursorCoords,
|
||||
undo: operation(undo),
|
||||
@@ -1402,7 +1406,8 @@ var CodeMirror = (function() {
|
||||
replaceRange: operation(replaceRange),
|
||||
|
||||
operation: function(f){return operation(f)();},
|
||||
- refresh: function(){updateDisplay([{from: 0, to: lines.length}]);}
|
||||
+ refresh: function(){updateDisplay([{from: 0, to: lines.length}]);},
|
||||
+ getInputField: function(){return input;}
|
||||
};
|
||||
return instance;
|
||||
}
|
||||
@@ -1420,6 +1425,7 @@ var CodeMirror = (function() {
|
||||
readOnly: false,
|
||||
onChange: null,
|
||||
onCursorActivity: null,
|
||||
+ onGutterClick: null,
|
||||
autoMatchBrackets: false,
|
||||
workTime: 200,
|
||||
workDelay: 300,
|
||||
</textarea></form>
|
||||
<script>
|
||||
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {});
|
||||
</script>
|
||||
|
||||
<p><strong>MIME types defined:</strong> <code>text/x-diff</code>.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
203
src/flask_debugtoolbar/static/codemirror/mode/ecl/ecl.js
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
CodeMirror.defineMode("ecl", function(config) {
|
||||
|
||||
function words(str) {
|
||||
var obj = {}, words = str.split(" ");
|
||||
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
|
||||
return obj;
|
||||
}
|
||||
|
||||
function metaHook(stream, state) {
|
||||
if (!state.startOfLine) return false;
|
||||
stream.skipToEnd();
|
||||
return "meta";
|
||||
}
|
||||
|
||||
function tokenAtString(stream, state) {
|
||||
var next;
|
||||
while ((next = stream.next()) != null) {
|
||||
if (next == '"' && !stream.eat('"')) {
|
||||
state.tokenize = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return "string";
|
||||
}
|
||||
|
||||
var indentUnit = config.indentUnit;
|
||||
var keyword = words("abs acos allnodes ascii asin asstring atan atan2 ave case choose choosen choosesets clustersize combine correlation cos cosh count covariance cron dataset dedup define denormalize distribute distributed distribution ebcdic enth error evaluate event eventextra eventname exists exp failcode failmessage fetch fromunicode getisvalid global graph group hash hash32 hash64 hashcrc hashmd5 having if index intformat isvalid iterate join keyunicode length library limit ln local log loop map matched matchlength matchposition matchtext matchunicode max merge mergejoin min nolocal nonempty normalize parse pipe power preload process project pull random range rank ranked realformat recordof regexfind regexreplace regroup rejected rollup round roundup row rowdiff sample set sin sinh sizeof soapcall sort sorted sqrt stepped stored sum table tan tanh thisnode topn tounicode transfer trim truncate typeof ungroup unicodeorder variance which workunit xmldecode xmlencode xmltext xmlunicode");
|
||||
var variable = words("apply assert build buildindex evaluate fail keydiff keypatch loadxml nothor notify output parallel sequential soapcall wait");
|
||||
var variable_2 = words("__compressed__ all and any as atmost before beginc++ best between case const counter csv descend encrypt end endc++ endmacro except exclusive expire export extend false few first flat from full function group header heading hole ifblock import in interface joined keep keyed last left limit load local locale lookup macro many maxcount maxlength min skew module named nocase noroot noscan nosort not of only opt or outer overwrite packed partition penalty physicallength pipe quote record relationship repeat return right scan self separator service shared skew skip sql store terminator thor threshold token transform trim true type unicodeorder unsorted validate virtual whole wild within xml xpath");
|
||||
var variable_3 = words("ascii big_endian boolean data decimal ebcdic integer pattern qstring real record rule set of string token udecimal unicode unsigned varstring varunicode");
|
||||
var builtin = words("checkpoint deprecated failcode failmessage failure global independent onwarning persist priority recovery stored success wait when");
|
||||
var blockKeywords = words("catch class do else finally for if switch try while");
|
||||
var atoms = words("true false null");
|
||||
var hooks = {"#": metaHook};
|
||||
var multiLineStrings;
|
||||
var isOperatorChar = /[+\-*&%=<>!?|\/]/;
|
||||
|
||||
var curPunc;
|
||||
|
||||
function tokenBase(stream, state) {
|
||||
var ch = stream.next();
|
||||
if (hooks[ch]) {
|
||||
var result = hooks[ch](stream, state);
|
||||
if (result !== false) return result;
|
||||
}
|
||||
if (ch == '"' || ch == "'") {
|
||||
state.tokenize = tokenString(ch);
|
||||
return state.tokenize(stream, state);
|
||||
}
|
||||
if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
|
||||
curPunc = ch;
|
||||
return null
|
||||
}
|
||||
if (/\d/.test(ch)) {
|
||||
stream.eatWhile(/[\w\.]/);
|
||||
return "number";
|
||||
}
|
||||
if (ch == "/") {
|
||||
if (stream.eat("*")) {
|
||||
state.tokenize = tokenComment;
|
||||
return tokenComment(stream, state);
|
||||
}
|
||||
if (stream.eat("/")) {
|
||||
stream.skipToEnd();
|
||||
return "comment";
|
||||
}
|
||||
}
|
||||
if (isOperatorChar.test(ch)) {
|
||||
stream.eatWhile(isOperatorChar);
|
||||
return "operator";
|
||||
}
|
||||
stream.eatWhile(/[\w\$_]/);
|
||||
var cur = stream.current().toLowerCase();
|
||||
if (keyword.propertyIsEnumerable(cur)) {
|
||||
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
|
||||
return "keyword";
|
||||
} else if (variable.propertyIsEnumerable(cur)) {
|
||||
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
|
||||
return "variable";
|
||||
} else if (variable_2.propertyIsEnumerable(cur)) {
|
||||
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
|
||||
return "variable-2";
|
||||
} else if (variable_3.propertyIsEnumerable(cur)) {
|
||||
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
|
||||
return "variable-3";
|
||||
} else if (builtin.propertyIsEnumerable(cur)) {
|
||||
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
|
||||
return "builtin";
|
||||
} else { //Data types are of from KEYWORD##
|
||||
var i = cur.length - 1;
|
||||
while(i >= 0 && (!isNaN(cur[i]) || cur[i] == '_'))
|
||||
--i;
|
||||
|
||||
if (i > 0) {
|
||||
var cur2 = cur.substr(0, i + 1);
|
||||
if (variable_3.propertyIsEnumerable(cur2)) {
|
||||
if (blockKeywords.propertyIsEnumerable(cur2)) curPunc = "newstatement";
|
||||
return "variable-3";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (atoms.propertyIsEnumerable(cur)) return "atom";
|
||||
return "word";
|
||||
}
|
||||
|
||||
function tokenString(quote) {
|
||||
return function(stream, state) {
|
||||
var escaped = false, next, end = false;
|
||||
while ((next = stream.next()) != null) {
|
||||
if (next == quote && !escaped) {end = true; break;}
|
||||
escaped = !escaped && next == "\\";
|
||||
}
|
||||
if (end || !(escaped || multiLineStrings))
|
||||
state.tokenize = tokenBase;
|
||||
return "string";
|
||||
};
|
||||
}
|
||||
|
||||
function tokenComment(stream, state) {
|
||||
var maybeEnd = false, ch;
|
||||
while (ch = stream.next()) {
|
||||
if (ch == "/" && maybeEnd) {
|
||||
state.tokenize = tokenBase;
|
||||
break;
|
||||
}
|
||||
maybeEnd = (ch == "*");
|
||||
}
|
||||
return "comment";
|
||||
}
|
||||
|
||||
function Context(indented, column, type, align, prev) {
|
||||
this.indented = indented;
|
||||
this.column = column;
|
||||
this.type = type;
|
||||
this.align = align;
|
||||
this.prev = prev;
|
||||
}
|
||||
function pushContext(state, col, type) {
|
||||
return state.context = new Context(state.indented, col, type, null, state.context);
|
||||
}
|
||||
function popContext(state) {
|
||||
var t = state.context.type;
|
||||
if (t == ")" || t == "]" || t == "}")
|
||||
state.indented = state.context.indented;
|
||||
return state.context = state.context.prev;
|
||||
}
|
||||
|
||||
// Interface
|
||||
|
||||
return {
|
||||
startState: function(basecolumn) {
|
||||
return {
|
||||
tokenize: null,
|
||||
context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
|
||||
indented: 0,
|
||||
startOfLine: true
|
||||
};
|
||||
},
|
||||
|
||||
token: function(stream, state) {
|
||||
var ctx = state.context;
|
||||
if (stream.sol()) {
|
||||
if (ctx.align == null) ctx.align = false;
|
||||
state.indented = stream.indentation();
|
||||
state.startOfLine = true;
|
||||
}
|
||||
if (stream.eatSpace()) return null;
|
||||
curPunc = null;
|
||||
var style = (state.tokenize || tokenBase)(stream, state);
|
||||
if (style == "comment" || style == "meta") return style;
|
||||
if (ctx.align == null) ctx.align = true;
|
||||
|
||||
if ((curPunc == ";" || curPunc == ":") && ctx.type == "statement") popContext(state);
|
||||
else if (curPunc == "{") pushContext(state, stream.column(), "}");
|
||||
else if (curPunc == "[") pushContext(state, stream.column(), "]");
|
||||
else if (curPunc == "(") pushContext(state, stream.column(), ")");
|
||||
else if (curPunc == "}") {
|
||||
while (ctx.type == "statement") ctx = popContext(state);
|
||||
if (ctx.type == "}") ctx = popContext(state);
|
||||
while (ctx.type == "statement") ctx = popContext(state);
|
||||
}
|
||||
else if (curPunc == ctx.type) popContext(state);
|
||||
else if (ctx.type == "}" || ctx.type == "top" || (ctx.type == "statement" && curPunc == "newstatement"))
|
||||
pushContext(state, stream.column(), "statement");
|
||||
state.startOfLine = false;
|
||||
return style;
|
||||
},
|
||||
|
||||
indent: function(state, textAfter) {
|
||||
if (state.tokenize != tokenBase && state.tokenize != null) return 0;
|
||||
var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
|
||||
if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
|
||||
var closing = firstChar == ctx.type;
|
||||
if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : indentUnit);
|
||||
else if (ctx.align) return ctx.column + (closing ? 0 : 1);
|
||||
else return ctx.indented + (closing ? 0 : indentUnit);
|
||||
},
|
||||
|
||||
electricChars: "{}"
|
||||
};
|
||||
});
|
||||
|
||||
CodeMirror.defineMIME("text/x-ecl");
|
||||
42
src/flask_debugtoolbar/static/codemirror/mode/ecl/index.html
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CodeMirror: ECL mode</title>
|
||||
<link rel="stylesheet" href="../../lib/codemirror.css">
|
||||
<script src="../../lib/codemirror.js"></script>
|
||||
<script src="ecl.js"></script>
|
||||
<link rel="stylesheet" href="../../doc/docs.css">
|
||||
<style>.CodeMirror {border: 1px solid black;}</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>CodeMirror: ECL mode</h1>
|
||||
<form><textarea id="code" name="code">
|
||||
/*
|
||||
sample useless code to demonstrate ecl syntax highlighting
|
||||
this is a multiline comment!
|
||||
*/
|
||||
|
||||
// this is a singleline comment!
|
||||
|
||||
import ut;
|
||||
r :=
|
||||
record
|
||||
string22 s1 := '123';
|
||||
integer4 i1 := 123;
|
||||
end;
|
||||
#option('tmp', true);
|
||||
d := dataset('tmp::qb', r, thor);
|
||||
output(d);
|
||||
</textarea></form>
|
||||
<script>
|
||||
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
||||
tabMode: "indent",
|
||||
matchBrackets: true,
|
||||
});
|
||||
</script>
|
||||
|
||||
<p>Based on CodeMirror's clike mode. For more information see <a href="http://hpccsystems.com">HPCC Systems</a> web site.</p>
|
||||
<p><strong>MIME types defined:</strong> <code>text/x-ecl</code>.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
108
src/flask_debugtoolbar/static/codemirror/mode/gfm/gfm.js
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
CodeMirror.defineMode("gfm", function(config, parserConfig) {
|
||||
var mdMode = CodeMirror.getMode(config, "markdown");
|
||||
var aliases = {
|
||||
html: "htmlmixed",
|
||||
js: "javascript",
|
||||
json: "application/json",
|
||||
c: "text/x-csrc",
|
||||
"c++": "text/x-c++src",
|
||||
java: "text/x-java",
|
||||
csharp: "text/x-csharp",
|
||||
"c#": "text/x-csharp"
|
||||
};
|
||||
|
||||
// make this lazy so that we don't need to load GFM last
|
||||
var getMode = (function () {
|
||||
var i, modes = {}, mimes = {}, mime;
|
||||
|
||||
var list = CodeMirror.listModes();
|
||||
for (i = 0; i < list.length; i++) {
|
||||
modes[list[i]] = list[i];
|
||||
}
|
||||
var mimesList = CodeMirror.listMIMEs();
|
||||
for (i = 0; i < mimesList.length; i++) {
|
||||
mime = mimesList[i].mime;
|
||||
mimes[mime] = mimesList[i].mime;
|
||||
}
|
||||
|
||||
for (var a in aliases) {
|
||||
if (aliases[a] in modes || aliases[a] in mimes)
|
||||
modes[a] = aliases[a];
|
||||
}
|
||||
|
||||
return function (lang) {
|
||||
return modes[lang] ? CodeMirror.getMode(config, modes[lang]) : null;
|
||||
}
|
||||
}());
|
||||
|
||||
function markdown(stream, state) {
|
||||
// intercept fenced code blocks
|
||||
if (stream.sol() && stream.match(/^```([\w+#]*)/)) {
|
||||
// try switching mode
|
||||
state.localMode = getMode(RegExp.$1)
|
||||
if (state.localMode)
|
||||
state.localState = state.localMode.startState();
|
||||
|
||||
state.token = local;
|
||||
return 'code';
|
||||
}
|
||||
|
||||
return mdMode.token(stream, state.mdState);
|
||||
}
|
||||
|
||||
function local(stream, state) {
|
||||
if (stream.sol() && stream.match(/^```/)) {
|
||||
state.localMode = state.localState = null;
|
||||
state.token = markdown;
|
||||
return 'code';
|
||||
}
|
||||
else if (state.localMode) {
|
||||
return state.localMode.token(stream, state.localState);
|
||||
} else {
|
||||
stream.skipToEnd();
|
||||
return 'code';
|
||||
}
|
||||
}
|
||||
|
||||
// custom handleText to prevent emphasis in the middle of a word
|
||||
// and add autolinking
|
||||
function handleText(stream, mdState) {
|
||||
var match;
|
||||
if (stream.match(/^\w+:\/\/\S+/)) {
|
||||
return 'linkhref';
|
||||
}
|
||||
if (stream.match(/^[^\[*\\<>` _][^\[*\\<>` ]*[^\[*\\<>` _]/)) {
|
||||
return mdMode.getType(mdState);
|
||||
}
|
||||
if (match = stream.match(/^[^\[*\\<>` ]+/)) {
|
||||
var word = match[0];
|
||||
if (word[0] === '_' && word[word.length-1] === '_') {
|
||||
stream.backUp(word.length);
|
||||
return undefined;
|
||||
}
|
||||
return mdMode.getType(mdState);
|
||||
}
|
||||
if (stream.eatSpace()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
startState: function() {
|
||||
var mdState = mdMode.startState();
|
||||
mdState.text = handleText;
|
||||
return {token: markdown, mode: "markdown", mdState: mdState,
|
||||
localMode: null, localState: null};
|
||||
},
|
||||
|
||||
copyState: function(state) {
|
||||
return {token: state.token, mode: state.mode, mdState: CodeMirror.copyState(mdMode, state.mdState),
|
||||
localMode: state.localMode,
|
||||
localState: state.localMode ? CodeMirror.copyState(state.localMode, state.localState) : null};
|
||||
},
|
||||
|
||||
token: function(stream, state) {
|
||||
return state.token(stream, state);
|
||||
}
|
||||
}
|
||||
});
|
||||
47
src/flask_debugtoolbar/static/codemirror/mode/gfm/index.html
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CodeMirror: GFM mode</title>
|
||||
<link rel="stylesheet" href="../../lib/codemirror.css">
|
||||
<script src="../../lib/codemirror.js"></script>
|
||||
<script src="../xml/xml.js"></script>
|
||||
<script src="../markdown/markdown.js"></script>
|
||||
<script src="gfm.js"></script>
|
||||
<script src="../javascript/javascript.js"></script>
|
||||
<link rel="stylesheet" href="../markdown/markdown.css">
|
||||
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
|
||||
<link rel="stylesheet" href="../../doc/docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>CodeMirror: GFM mode</h1>
|
||||
|
||||
<!-- source: http://daringfireball.net/projects/markdown/basics.text -->
|
||||
<form><textarea id="code" name="code">
|
||||
Github Flavored Markdown
|
||||
========================
|
||||
|
||||
Everything from markdown plus GFM features:
|
||||
|
||||
## Fenced code blocks
|
||||
|
||||
```javascript
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
console.log(items[i], i); // log them
|
||||
}
|
||||
```
|
||||
|
||||
See http://github.github.com/github-flavored-markdown/
|
||||
|
||||
</textarea></form>
|
||||
|
||||
<script>
|
||||
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
||||
mode: 'gfm',
|
||||
lineNumbers: true,
|
||||
matchBrackets: true,
|
||||
theme: "default"
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
170
src/flask_debugtoolbar/static/codemirror/mode/go/go.js
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
CodeMirror.defineMode("go", function(config, parserConfig) {
|
||||
var indentUnit = config.indentUnit;
|
||||
|
||||
var keywords = {
|
||||
"break":true, "case":true, "chan":true, "const":true, "continue":true,
|
||||
"default":true, "defer":true, "else":true, "fallthrough":true, "for":true,
|
||||
"func":true, "go":true, "goto":true, "if":true, "import":true,
|
||||
"interface":true, "map":true, "package":true, "range":true, "return":true,
|
||||
"select":true, "struct":true, "switch":true, "type":true, "var":true,
|
||||
"bool":true, "byte":true, "complex64":true, "complex128":true,
|
||||
"float32":true, "float64":true, "int8":true, "int16":true, "int32":true,
|
||||
"int64":true, "string":true, "uint8":true, "uint16":true, "uint32":true,
|
||||
"uint64":true, "int":true, "uint":true, "uintptr":true
|
||||
};
|
||||
|
||||
var atoms = {
|
||||
"true":true, "false":true, "iota":true, "nil":true, "append":true,
|
||||
"cap":true, "close":true, "complex":true, "copy":true, "imag":true,
|
||||
"len":true, "make":true, "new":true, "panic":true, "print":true,
|
||||
"println":true, "real":true, "recover":true
|
||||
};
|
||||
|
||||
var blockKeywords = {
|
||||
"else":true, "for":true, "func":true, "if":true, "interface":true,
|
||||
"select":true, "struct":true, "switch":true
|
||||
};
|
||||
|
||||
var isOperatorChar = /[+\-*&^%:=<>!|\/]/;
|
||||
|
||||
var curPunc;
|
||||
|
||||
function tokenBase(stream, state) {
|
||||
var ch = stream.next();
|
||||
if (ch == '"' || ch == "'" || ch == "`") {
|
||||
state.tokenize = tokenString(ch);
|
||||
return state.tokenize(stream, state);
|
||||
}
|
||||
if (/[\d\.]/.test(ch)) {
|
||||
if (ch == ".") {
|
||||
stream.match(/^[0-9]+([eE][\-+]?[0-9]+)?/);
|
||||
} else if (ch == "0") {
|
||||
stream.match(/^[xX][0-9a-fA-F]+/) || stream.match(/^0[0-7]+/);
|
||||
} else {
|
||||
stream.match(/^[0-9]*\.?[0-9]*([eE][\-+]?[0-9]+)?/);
|
||||
}
|
||||
return "number";
|
||||
}
|
||||
if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
|
||||
curPunc = ch;
|
||||
return null
|
||||
}
|
||||
if (ch == "/") {
|
||||
if (stream.eat("*")) {
|
||||
state.tokenize = tokenComment;
|
||||
return tokenComment(stream, state);
|
||||
}
|
||||
if (stream.eat("/")) {
|
||||
stream.skipToEnd();
|
||||
return "comment";
|
||||
}
|
||||
}
|
||||
if (isOperatorChar.test(ch)) {
|
||||
stream.eatWhile(isOperatorChar);
|
||||
return "operator";
|
||||
}
|
||||
stream.eatWhile(/[\w\$_]/);
|
||||
var cur = stream.current();
|
||||
if (keywords.propertyIsEnumerable(cur)) {
|
||||
if (cur == "case" || cur == "default") curPunc = "case";
|
||||
return "keyword";
|
||||
}
|
||||
if (atoms.propertyIsEnumerable(cur)) return "atom";
|
||||
return "word";
|
||||
}
|
||||
|
||||
function tokenString(quote) {
|
||||
return function(stream, state) {
|
||||
var escaped = false, next, end = false;
|
||||
while ((next = stream.next()) != null) {
|
||||
if (next == quote && !escaped) {end = true; break;}
|
||||
escaped = !escaped && next == "\\";
|
||||
}
|
||||
if (end || !(escaped || quote == "`"))
|
||||
state.tokenize = tokenBase;
|
||||
return "string";
|
||||
};
|
||||
}
|
||||
|
||||
function tokenComment(stream, state) {
|
||||
var maybeEnd = false, ch;
|
||||
while (ch = stream.next()) {
|
||||
if (ch == "/" && maybeEnd) {
|
||||
state.tokenize = tokenBase;
|
||||
break;
|
||||
}
|
||||
maybeEnd = (ch == "*");
|
||||
}
|
||||
return "comment";
|
||||
}
|
||||
|
||||
function Context(indented, column, type, align, prev) {
|
||||
this.indented = indented;
|
||||
this.column = column;
|
||||
this.type = type;
|
||||
this.align = align;
|
||||
this.prev = prev;
|
||||
}
|
||||
function pushContext(state, col, type) {
|
||||
return state.context = new Context(state.indented, col, type, null, state.context);
|
||||
}
|
||||
function popContext(state) {
|
||||
var t = state.context.type;
|
||||
if (t == ")" || t == "]" || t == "}")
|
||||
state.indented = state.context.indented;
|
||||
return state.context = state.context.prev;
|
||||
}
|
||||
|
||||
// Interface
|
||||
|
||||
return {
|
||||
startState: function(basecolumn) {
|
||||
return {
|
||||
tokenize: null,
|
||||
context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
|
||||
indented: 0,
|
||||
startOfLine: true
|
||||
};
|
||||
},
|
||||
|
||||
token: function(stream, state) {
|
||||
var ctx = state.context;
|
||||
if (stream.sol()) {
|
||||
if (ctx.align == null) ctx.align = false;
|
||||
state.indented = stream.indentation();
|
||||
state.startOfLine = true;
|
||||
if (ctx.type == "case") ctx.type = "}";
|
||||
}
|
||||
if (stream.eatSpace()) return null;
|
||||
curPunc = null;
|
||||
var style = (state.tokenize || tokenBase)(stream, state);
|
||||
if (style == "comment") return style;
|
||||
if (ctx.align == null) ctx.align = true;
|
||||
|
||||
if (curPunc == "{") pushContext(state, stream.column(), "}");
|
||||
else if (curPunc == "[") pushContext(state, stream.column(), "]");
|
||||
else if (curPunc == "(") pushContext(state, stream.column(), ")");
|
||||
else if (curPunc == "case") ctx.type = "case"
|
||||
else if (curPunc == "}" && ctx.type == "}") ctx = popContext(state);
|
||||
else if (curPunc == ctx.type) popContext(state);
|
||||
state.startOfLine = false;
|
||||
return style;
|
||||
},
|
||||
|
||||
indent: function(state, textAfter) {
|
||||
if (state.tokenize != tokenBase && state.tokenize != null) return 0;
|
||||
var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
|
||||
if (ctx.type == "case" && /^(?:case|default)\b/.test(textAfter)) {
|
||||
state.context.type = "}";
|
||||
return ctx.indented;
|
||||
}
|
||||
var closing = firstChar == ctx.type;
|
||||
if (ctx.align) return ctx.column + (closing ? 0 : 1);
|
||||
else return ctx.indented + (closing ? 0 : indentUnit);
|
||||
},
|
||||
|
||||
electricChars: "{}:"
|
||||
};
|
||||
});
|
||||
|
||||
CodeMirror.defineMIME("text/x-go", "go");
|
||||
72
src/flask_debugtoolbar/static/codemirror/mode/go/index.html
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CodeMirror: Go mode</title>
|
||||
<link rel="stylesheet" href="../../lib/codemirror.css">
|
||||
<link rel="stylesheet" href="../../theme/elegant.css">
|
||||
<script src="../../lib/codemirror.js"></script>
|
||||
<script src="go.js"></script>
|
||||
<link rel="stylesheet" href="../../doc/docs.css">
|
||||
<style>.CodeMirror {border:1px solid #999; background:#ffc}</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>CodeMirror: Go mode</h1>
|
||||
|
||||
<form><textarea id="code" name="code">
|
||||
// Prime Sieve in Go.
|
||||
// Taken from the Go specification.
|
||||
// Copyright © The Go Authors.
|
||||
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Send the sequence 2, 3, 4, ... to channel 'ch'.
|
||||
func generate(ch chan<- int) {
|
||||
for i := 2; ; i++ {
|
||||
ch <- i // Send 'i' to channel 'ch'
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the values from channel 'src' to channel 'dst',
|
||||
// removing those divisible by 'prime'.
|
||||
func filter(src <-chan int, dst chan<- int, prime int) {
|
||||
for i := range src { // Loop over values received from 'src'.
|
||||
if i%prime != 0 {
|
||||
dst <- i // Send 'i' to channel 'dst'.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The prime sieve: Daisy-chain filter processes together.
|
||||
func sieve() {
|
||||
ch := make(chan int) // Create a new channel.
|
||||
go generate(ch) // Start generate() as a subprocess.
|
||||
for {
|
||||
prime := <-ch
|
||||
fmt.Print(prime, "\n")
|
||||
ch1 := make(chan int)
|
||||
go filter(ch, ch1, prime)
|
||||
ch = ch1
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
sieve()
|
||||
}
|
||||
</textarea></form>
|
||||
|
||||
<script>
|
||||
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
||||
theme: "elegant",
|
||||
matchBrackets: true,
|
||||
indentUnit: 8,
|
||||
tabSize: 8,
|
||||
indentWithTabs: true,
|
||||
mode: "text/x-go"
|
||||
});
|
||||
</script>
|
||||
|
||||
<p><strong>MIME type:</strong> <code>text/x-go</code></p>
|
||||
</body>
|
||||
</html>
|
||||
210
src/flask_debugtoolbar/static/codemirror/mode/groovy/groovy.js
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
CodeMirror.defineMode("groovy", function(config, parserConfig) {
|
||||
function words(str) {
|
||||
var obj = {}, words = str.split(" ");
|
||||
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
|
||||
return obj;
|
||||
}
|
||||
var keywords = words(
|
||||
"abstract as assert boolean break byte case catch char class const continue def default " +
|
||||
"do double else enum extends final finally float for goto if implements import in " +
|
||||
"instanceof int interface long native new package private protected public return " +
|
||||
"short static strictfp super switch synchronized threadsafe throw throws transient " +
|
||||
"try void volatile while");
|
||||
var blockKeywords = words("catch class do else finally for if switch try while enum interface def");
|
||||
var atoms = words("null true false this");
|
||||
|
||||
var curPunc;
|
||||
function tokenBase(stream, state) {
|
||||
var ch = stream.next();
|
||||
if (ch == '"' || ch == "'") {
|
||||
return startString(ch, stream, state);
|
||||
}
|
||||
if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
|
||||
curPunc = ch;
|
||||
return null
|
||||
}
|
||||
if (/\d/.test(ch)) {
|
||||
stream.eatWhile(/[\w\.]/);
|
||||
if (stream.eat(/eE/)) { stream.eat(/\+\-/); stream.eatWhile(/\d/); }
|
||||
return "number";
|
||||
}
|
||||
if (ch == "/") {
|
||||
if (stream.eat("*")) {
|
||||
state.tokenize.push(tokenComment);
|
||||
return tokenComment(stream, state);
|
||||
}
|
||||
if (stream.eat("/")) {
|
||||
stream.skipToEnd();
|
||||
return "comment";
|
||||
}
|
||||
if (expectExpression(state.lastToken)) {
|
||||
return startString(ch, stream, state);
|
||||
}
|
||||
}
|
||||
if (ch == "-" && stream.eat(">")) {
|
||||
curPunc = "->";
|
||||
return null;
|
||||
}
|
||||
if (/[+\-*&%=<>!?|\/~]/.test(ch)) {
|
||||
stream.eatWhile(/[+\-*&%=<>|~]/);
|
||||
return "operator";
|
||||
}
|
||||
stream.eatWhile(/[\w\$_]/);
|
||||
if (ch == "@") { stream.eatWhile(/[\w\$_\.]/); return "meta"; }
|
||||
if (state.lastToken == ".") return "property";
|
||||
if (stream.eat(":")) { curPunc = "proplabel"; return "property"; }
|
||||
var cur = stream.current();
|
||||
if (atoms.propertyIsEnumerable(cur)) { return "atom"; }
|
||||
if (keywords.propertyIsEnumerable(cur)) {
|
||||
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
|
||||
return "keyword";
|
||||
}
|
||||
return "word";
|
||||
}
|
||||
tokenBase.isBase = true;
|
||||
|
||||
function startString(quote, stream, state) {
|
||||
var tripleQuoted = false;
|
||||
if (quote != "/" && stream.eat(quote)) {
|
||||
if (stream.eat(quote)) tripleQuoted = true;
|
||||
else return "string";
|
||||
}
|
||||
function t(stream, state) {
|
||||
var escaped = false, next, end = !tripleQuoted;
|
||||
while ((next = stream.next()) != null) {
|
||||
if (next == quote && !escaped) {
|
||||
if (!tripleQuoted) { break; }
|
||||
if (stream.match(quote + quote)) { end = true; break; }
|
||||
}
|
||||
if (quote == '"' && next == "$" && !escaped && stream.eat("{")) {
|
||||
state.tokenize.push(tokenBaseUntilBrace());
|
||||
return "string";
|
||||
}
|
||||
escaped = !escaped && next == "\\";
|
||||
}
|
||||
if (end) state.tokenize.pop();
|
||||
return "string";
|
||||
}
|
||||
state.tokenize.push(t);
|
||||
return t(stream, state);
|
||||
}
|
||||
|
||||
function tokenBaseUntilBrace() {
|
||||
var depth = 1;
|
||||
function t(stream, state) {
|
||||
if (stream.peek() == "}") {
|
||||
depth--;
|
||||
if (depth == 0) {
|
||||
state.tokenize.pop();
|
||||
return state.tokenize[state.tokenize.length-1](stream, state);
|
||||
}
|
||||
} else if (stream.peek() == "{") {
|
||||
depth++;
|
||||
}
|
||||
return tokenBase(stream, state);
|
||||
}
|
||||
t.isBase = true;
|
||||
return t;
|
||||
}
|
||||
|
||||
function tokenComment(stream, state) {
|
||||
var maybeEnd = false, ch;
|
||||
while (ch = stream.next()) {
|
||||
if (ch == "/" && maybeEnd) {
|
||||
state.tokenize.pop();
|
||||
break;
|
||||
}
|
||||
maybeEnd = (ch == "*");
|
||||
}
|
||||
return "comment";
|
||||
}
|
||||
|
||||
function expectExpression(last) {
|
||||
return !last || last == "operator" || last == "->" || /[\.\[\{\(,;:]/.test(last) ||
|
||||
last == "newstatement" || last == "keyword" || last == "proplabel";
|
||||
}
|
||||
|
||||
function Context(indented, column, type, align, prev) {
|
||||
this.indented = indented;
|
||||
this.column = column;
|
||||
this.type = type;
|
||||
this.align = align;
|
||||
this.prev = prev;
|
||||
}
|
||||
function pushContext(state, col, type) {
|
||||
return state.context = new Context(state.indented, col, type, null, state.context);
|
||||
}
|
||||
function popContext(state) {
|
||||
var t = state.context.type;
|
||||
if (t == ")" || t == "]" || t == "}")
|
||||
state.indented = state.context.indented;
|
||||
return state.context = state.context.prev;
|
||||
}
|
||||
|
||||
// Interface
|
||||
|
||||
return {
|
||||
startState: function(basecolumn) {
|
||||
return {
|
||||
tokenize: [tokenBase],
|
||||
context: new Context((basecolumn || 0) - config.indentUnit, 0, "top", false),
|
||||
indented: 0,
|
||||
startOfLine: true,
|
||||
lastToken: null
|
||||
};
|
||||
},
|
||||
|
||||
token: function(stream, state) {
|
||||
var ctx = state.context;
|
||||
if (stream.sol()) {
|
||||
if (ctx.align == null) ctx.align = false;
|
||||
state.indented = stream.indentation();
|
||||
state.startOfLine = true;
|
||||
// Automatic semicolon insertion
|
||||
if (ctx.type == "statement" && !expectExpression(state.lastToken)) {
|
||||
popContext(state); ctx = state.context;
|
||||
}
|
||||
}
|
||||
if (stream.eatSpace()) return null;
|
||||
curPunc = null;
|
||||
var style = state.tokenize[state.tokenize.length-1](stream, state);
|
||||
if (style == "comment") return style;
|
||||
if (ctx.align == null) ctx.align = true;
|
||||
|
||||
if ((curPunc == ";" || curPunc == ":") && ctx.type == "statement") popContext(state);
|
||||
// Handle indentation for {x -> \n ... }
|
||||
else if (curPunc == "->" && ctx.type == "statement" && ctx.prev.type == "}") {
|
||||
popContext(state);
|
||||
state.context.align = false;
|
||||
}
|
||||
else if (curPunc == "{") pushContext(state, stream.column(), "}");
|
||||
else if (curPunc == "[") pushContext(state, stream.column(), "]");
|
||||
else if (curPunc == "(") pushContext(state, stream.column(), ")");
|
||||
else if (curPunc == "}") {
|
||||
while (ctx.type == "statement") ctx = popContext(state);
|
||||
if (ctx.type == "}") ctx = popContext(state);
|
||||
while (ctx.type == "statement") ctx = popContext(state);
|
||||
}
|
||||
else if (curPunc == ctx.type) popContext(state);
|
||||
else if (ctx.type == "}" || ctx.type == "top" || (ctx.type == "statement" && curPunc == "newstatement"))
|
||||
pushContext(state, stream.column(), "statement");
|
||||
state.startOfLine = false;
|
||||
state.lastToken = curPunc || style;
|
||||
return style;
|
||||
},
|
||||
|
||||
indent: function(state, textAfter) {
|
||||
if (!state.tokenize[state.tokenize.length-1].isBase) return 0;
|
||||
var firstChar = textAfter && textAfter.charAt(0), ctx = state.context;
|
||||
if (ctx.type == "statement" && !expectExpression(state.lastToken)) ctx = ctx.prev;
|
||||
var closing = firstChar == ctx.type;
|
||||
if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : config.indentUnit);
|
||||
else if (ctx.align) return ctx.column + (closing ? 0 : 1);
|
||||
else return ctx.indented + (closing ? 0 : config.indentUnit);
|
||||
},
|
||||
|
||||
electricChars: "{}"
|
||||
};
|
||||
});
|
||||
|
||||
CodeMirror.defineMIME("text/x-groovy", "groovy");
|
||||
71
src/flask_debugtoolbar/static/codemirror/mode/groovy/index.html
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CodeMirror: Groovy mode</title>
|
||||
<link rel="stylesheet" href="../../lib/codemirror.css">
|
||||
<script src="../../lib/codemirror.js"></script>
|
||||
<script src="groovy.js"></script>
|
||||
<link rel="stylesheet" href="../../doc/docs.css">
|
||||
<style>.CodeMirror {border-top: 1px solid #500; border-bottom: 1px solid #500;}</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>CodeMirror: Groovy mode</h1>
|
||||
|
||||
<form><textarea id="code" name="code">
|
||||
//Pattern for groovy script
|
||||
def p = ~/.*\.groovy/
|
||||
new File( 'd:\\scripts' ).eachFileMatch(p) {f ->
|
||||
// imports list
|
||||
def imports = []
|
||||
f.eachLine {
|
||||
// condition to detect an import instruction
|
||||
ln -> if ( ln =~ '^import .*' ) {
|
||||
imports << "${ln - 'import '}"
|
||||
}
|
||||
}
|
||||
// print thmen
|
||||
if ( ! imports.empty ) {
|
||||
println f
|
||||
imports.each{ println " $it" }
|
||||
}
|
||||
}
|
||||
|
||||
/* Coin changer demo code from http://groovy.codehaus.org */
|
||||
|
||||
enum UsCoin {
|
||||
quarter(25), dime(10), nickel(5), penny(1)
|
||||
UsCoin(v) { value = v }
|
||||
final value
|
||||
}
|
||||
|
||||
enum OzzieCoin {
|
||||
fifty(50), twenty(20), ten(10), five(5)
|
||||
OzzieCoin(v) { value = v }
|
||||
final value
|
||||
}
|
||||
|
||||
def plural(word, count) {
|
||||
if (count == 1) return word
|
||||
word[-1] == 'y' ? word[0..-2] + "ies" : word + "s"
|
||||
}
|
||||
|
||||
def change(currency, amount) {
|
||||
currency.values().inject([]){ list, coin ->
|
||||
int count = amount / coin.value
|
||||
amount = amount % coin.value
|
||||
list += "$count ${plural(coin.toString(), count)}"
|
||||
}
|
||||
}
|
||||
</textarea></form>
|
||||
|
||||
<script>
|
||||
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
||||
lineNumbers: true,
|
||||
matchBrackets: true,
|
||||
mode: "text/x-groovy"
|
||||
});
|
||||
</script>
|
||||
|
||||
<p><strong>MIME types defined:</strong> <code>text/x-groovy</code></p>
|
||||
</body>
|
||||
</html>
|
||||
242
src/flask_debugtoolbar/static/codemirror/mode/haskell/haskell.js
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
CodeMirror.defineMode("haskell", function(cmCfg, modeCfg) {
|
||||
|
||||
function switchState(source, setState, f) {
|
||||
setState(f);
|
||||
return f(source, setState);
|
||||
}
|
||||
|
||||
// These should all be Unicode extended, as per the Haskell 2010 report
|
||||
var smallRE = /[a-z_]/;
|
||||
var largeRE = /[A-Z]/;
|
||||
var digitRE = /[0-9]/;
|
||||
var hexitRE = /[0-9A-Fa-f]/;
|
||||
var octitRE = /[0-7]/;
|
||||
var idRE = /[a-z_A-Z0-9']/;
|
||||
var symbolRE = /[-!#$%&*+.\/<=>?@\\^|~:]/;
|
||||
var specialRE = /[(),;[\]`{}]/;
|
||||
var whiteCharRE = /[ \t\v\f]/; // newlines are handled in tokenizer
|
||||
|
||||
function normal(source, setState) {
|
||||
if (source.eatWhile(whiteCharRE)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var ch = source.next();
|
||||
if (specialRE.test(ch)) {
|
||||
if (ch == '{' && source.eat('-')) {
|
||||
var t = "comment";
|
||||
if (source.eat('#')) {
|
||||
t = "meta";
|
||||
}
|
||||
return switchState(source, setState, ncomment(t, 1));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ch == '\'') {
|
||||
if (source.eat('\\')) {
|
||||
source.next(); // should handle other escapes here
|
||||
}
|
||||
else {
|
||||
source.next();
|
||||
}
|
||||
if (source.eat('\'')) {
|
||||
return "string";
|
||||
}
|
||||
return "error";
|
||||
}
|
||||
|
||||
if (ch == '"') {
|
||||
return switchState(source, setState, stringLiteral);
|
||||
}
|
||||
|
||||
if (largeRE.test(ch)) {
|
||||
source.eatWhile(idRE);
|
||||
if (source.eat('.')) {
|
||||
return "qualifier";
|
||||
}
|
||||
return "variable-2";
|
||||
}
|
||||
|
||||
if (smallRE.test(ch)) {
|
||||
source.eatWhile(idRE);
|
||||
return "variable";
|
||||
}
|
||||
|
||||
if (digitRE.test(ch)) {
|
||||
if (ch == '0') {
|
||||
if (source.eat(/[xX]/)) {
|
||||
source.eatWhile(hexitRE); // should require at least 1
|
||||
return "integer";
|
||||
}
|
||||
if (source.eat(/[oO]/)) {
|
||||
source.eatWhile(octitRE); // should require at least 1
|
||||
return "number";
|
||||
}
|
||||
}
|
||||
source.eatWhile(digitRE);
|
||||
var t = "number";
|
||||
if (source.eat('.')) {
|
||||
t = "number";
|
||||
source.eatWhile(digitRE); // should require at least 1
|
||||
}
|
||||
if (source.eat(/[eE]/)) {
|
||||
t = "number";
|
||||
source.eat(/[-+]/);
|
||||
source.eatWhile(digitRE); // should require at least 1
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
if (symbolRE.test(ch)) {
|
||||
if (ch == '-' && source.eat(/-/)) {
|
||||
source.eatWhile(/-/);
|
||||
if (!source.eat(symbolRE)) {
|
||||
source.skipToEnd();
|
||||
return "comment";
|
||||
}
|
||||
}
|
||||
var t = "variable";
|
||||
if (ch == ':') {
|
||||
t = "variable-2";
|
||||
}
|
||||
source.eatWhile(symbolRE);
|
||||
return t;
|
||||
}
|
||||
|
||||
return "error";
|
||||
}
|
||||
|
||||
function ncomment(type, nest) {
|
||||
if (nest == 0) {
|
||||
return normal;
|
||||
}
|
||||
return function(source, setState) {
|
||||
var currNest = nest;
|
||||
while (!source.eol()) {
|
||||
var ch = source.next();
|
||||
if (ch == '{' && source.eat('-')) {
|
||||
++currNest;
|
||||
}
|
||||
else if (ch == '-' && source.eat('}')) {
|
||||
--currNest;
|
||||
if (currNest == 0) {
|
||||
setState(normal);
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
setState(ncomment(type, currNest));
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
function stringLiteral(source, setState) {
|
||||
while (!source.eol()) {
|
||||
var ch = source.next();
|
||||
if (ch == '"') {
|
||||
setState(normal);
|
||||
return "string";
|
||||
}
|
||||
if (ch == '\\') {
|
||||
if (source.eol() || source.eat(whiteCharRE)) {
|
||||
setState(stringGap);
|
||||
return "string";
|
||||
}
|
||||
if (source.eat('&')) {
|
||||
}
|
||||
else {
|
||||
source.next(); // should handle other escapes here
|
||||
}
|
||||
}
|
||||
}
|
||||
setState(normal);
|
||||
return "error";
|
||||
}
|
||||
|
||||
function stringGap(source, setState) {
|
||||
if (source.eat('\\')) {
|
||||
return switchState(source, setState, stringLiteral);
|
||||
}
|
||||
source.next();
|
||||
setState(normal);
|
||||
return "error";
|
||||
}
|
||||
|
||||
|
||||
var wellKnownWords = (function() {
|
||||
var wkw = {};
|
||||
function setType(t) {
|
||||
return function () {
|
||||
for (var i = 0; i < arguments.length; i++)
|
||||
wkw[arguments[i]] = t;
|
||||
}
|
||||
}
|
||||
|
||||
setType("keyword")(
|
||||
"case", "class", "data", "default", "deriving", "do", "else", "foreign",
|
||||
"if", "import", "in", "infix", "infixl", "infixr", "instance", "let",
|
||||
"module", "newtype", "of", "then", "type", "where", "_");
|
||||
|
||||
setType("keyword")(
|
||||
"\.\.", ":", "::", "=", "\\", "\"", "<-", "->", "@", "~", "=>");
|
||||
|
||||
setType("builtin")(
|
||||
"!!", "$!", "$", "&&", "+", "++", "-", ".", "/", "/=", "<", "<=", "=<<",
|
||||
"==", ">", ">=", ">>", ">>=", "^", "^^", "||", "*", "**");
|
||||
|
||||
setType("builtin")(
|
||||
"Bool", "Bounded", "Char", "Double", "EQ", "Either", "Enum", "Eq",
|
||||
"False", "FilePath", "Float", "Floating", "Fractional", "Functor", "GT",
|
||||
"IO", "IOError", "Int", "Integer", "Integral", "Just", "LT", "Left",
|
||||
"Maybe", "Monad", "Nothing", "Num", "Ord", "Ordering", "Rational", "Read",
|
||||
"ReadS", "Real", "RealFloat", "RealFrac", "Right", "Show", "ShowS",
|
||||
"String", "True");
|
||||
|
||||
setType("builtin")(
|
||||
"abs", "acos", "acosh", "all", "and", "any", "appendFile", "asTypeOf",
|
||||
"asin", "asinh", "atan", "atan2", "atanh", "break", "catch", "ceiling",
|
||||
"compare", "concat", "concatMap", "const", "cos", "cosh", "curry",
|
||||
"cycle", "decodeFloat", "div", "divMod", "drop", "dropWhile", "either",
|
||||
"elem", "encodeFloat", "enumFrom", "enumFromThen", "enumFromThenTo",
|
||||
"enumFromTo", "error", "even", "exp", "exponent", "fail", "filter",
|
||||
"flip", "floatDigits", "floatRadix", "floatRange", "floor", "fmap",
|
||||
"foldl", "foldl1", "foldr", "foldr1", "fromEnum", "fromInteger",
|
||||
"fromIntegral", "fromRational", "fst", "gcd", "getChar", "getContents",
|
||||
"getLine", "head", "id", "init", "interact", "ioError", "isDenormalized",
|
||||
"isIEEE", "isInfinite", "isNaN", "isNegativeZero", "iterate", "last",
|
||||
"lcm", "length", "lex", "lines", "log", "logBase", "lookup", "map",
|
||||
"mapM", "mapM_", "max", "maxBound", "maximum", "maybe", "min", "minBound",
|
||||
"minimum", "mod", "negate", "not", "notElem", "null", "odd", "or",
|
||||
"otherwise", "pi", "pred", "print", "product", "properFraction",
|
||||
"putChar", "putStr", "putStrLn", "quot", "quotRem", "read", "readFile",
|
||||
"readIO", "readList", "readLn", "readParen", "reads", "readsPrec",
|
||||
"realToFrac", "recip", "rem", "repeat", "replicate", "return", "reverse",
|
||||
"round", "scaleFloat", "scanl", "scanl1", "scanr", "scanr1", "seq",
|
||||
"sequence", "sequence_", "show", "showChar", "showList", "showParen",
|
||||
"showString", "shows", "showsPrec", "significand", "signum", "sin",
|
||||
"sinh", "snd", "span", "splitAt", "sqrt", "subtract", "succ", "sum",
|
||||
"tail", "take", "takeWhile", "tan", "tanh", "toEnum", "toInteger",
|
||||
"toRational", "truncate", "uncurry", "undefined", "unlines", "until",
|
||||
"unwords", "unzip", "unzip3", "userError", "words", "writeFile", "zip",
|
||||
"zip3", "zipWith", "zipWith3");
|
||||
|
||||
return wkw;
|
||||
})();
|
||||
|
||||
|
||||
|
||||
return {
|
||||
startState: function () { return { f: normal }; },
|
||||
copyState: function (s) { return { f: s.f }; },
|
||||
|
||||
token: function(stream, state) {
|
||||
var t = state.f(stream, function(s) { state.f = s; });
|
||||
var w = stream.current();
|
||||
return (w in wellKnownWords) ? wellKnownWords[w] : t;
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
CodeMirror.defineMIME("text/x-haskell", "haskell");
|
||||
60
src/flask_debugtoolbar/static/codemirror/mode/haskell/index.html
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CodeMirror: Haskell mode</title>
|
||||
<link rel="stylesheet" href="../../lib/codemirror.css">
|
||||
<script src="../../lib/codemirror.js"></script>
|
||||
<script src="haskell.js"></script>
|
||||
<link rel="stylesheet" href="../../theme/elegant.css">
|
||||
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
|
||||
<link rel="stylesheet" href="../../doc/docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>CodeMirror: Haskell mode</h1>
|
||||
|
||||
<form><textarea id="code" name="code">
|
||||
module UniquePerms (
|
||||
uniquePerms
|
||||
)
|
||||
where
|
||||
|
||||
-- | Find all unique permutations of a list where there might be duplicates.
|
||||
uniquePerms :: (Eq a) => [a] -> [[a]]
|
||||
uniquePerms = permBag . makeBag
|
||||
|
||||
-- | An unordered collection where duplicate values are allowed,
|
||||
-- but represented with a single value and a count.
|
||||
type Bag a = [(a, Int)]
|
||||
|
||||
makeBag :: (Eq a) => [a] -> Bag a
|
||||
makeBag [] = []
|
||||
makeBag (a:as) = mix a $ makeBag as
|
||||
where
|
||||
mix a [] = [(a,1)]
|
||||
mix a (bn@(b,n):bs) | a == b = (b,n+1):bs
|
||||
| otherwise = bn : mix a bs
|
||||
|
||||
permBag :: Bag a -> [[a]]
|
||||
permBag [] = [[]]
|
||||
permBag bs = concatMap (\(f,cs) -> map (f:) $ permBag cs) . oneOfEach $ bs
|
||||
where
|
||||
oneOfEach [] = []
|
||||
oneOfEach (an@(a,n):bs) =
|
||||
let bs' = if n == 1 then bs else (a,n-1):bs
|
||||
in (a,bs') : mapSnd (an:) (oneOfEach bs)
|
||||
|
||||
apSnd f (a,b) = (a, f b)
|
||||
mapSnd = map . apSnd
|
||||
</textarea></form>
|
||||
|
||||
<script>
|
||||
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
||||
lineNumbers: true,
|
||||
matchBrackets: true,
|
||||
theme: "elegant"
|
||||
});
|
||||
</script>
|
||||
|
||||
<p><strong>MIME types defined:</strong> <code>text/x-haskell</code>.</p>
|
||||
</body>
|
||||
</html>
|
||||
68
src/flask_debugtoolbar/static/codemirror/mode/htmlembedded/htmlembedded.js
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
CodeMirror.defineMode("htmlembedded", function(config, parserConfig) {
|
||||
|
||||
//config settings
|
||||
var scriptStartRegex = parserConfig.scriptStartRegex || /^<%/i,
|
||||
scriptEndRegex = parserConfig.scriptEndRegex || /^%>/i;
|
||||
|
||||
//inner modes
|
||||
var scriptingMode, htmlMixedMode;
|
||||
|
||||
//tokenizer when in html mode
|
||||
function htmlDispatch(stream, state) {
|
||||
if (stream.match(scriptStartRegex, false)) {
|
||||
state.token=scriptingDispatch;
|
||||
return scriptingMode.token(stream, state.scriptState);
|
||||
}
|
||||
else
|
||||
return htmlMixedMode.token(stream, state.htmlState);
|
||||
}
|
||||
|
||||
//tokenizer when in scripting mode
|
||||
function scriptingDispatch(stream, state) {
|
||||
if (stream.match(scriptEndRegex, false)) {
|
||||
state.token=htmlDispatch;
|
||||
return htmlMixedMode.token(stream, state.htmlState);
|
||||
}
|
||||
else
|
||||
return scriptingMode.token(stream, state.scriptState);
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
startState: function() {
|
||||
scriptingMode = scriptingMode || CodeMirror.getMode(config, parserConfig.scriptingModeSpec);
|
||||
htmlMixedMode = htmlMixedMode || CodeMirror.getMode(config, "htmlmixed");
|
||||
return {
|
||||
token : parserConfig.startOpen ? scriptingDispatch : htmlDispatch,
|
||||
htmlState : htmlMixedMode.startState(),
|
||||
scriptState : scriptingMode.startState()
|
||||
}
|
||||
},
|
||||
|
||||
token: function(stream, state) {
|
||||
return state.token(stream, state);
|
||||
},
|
||||
|
||||
indent: function(state, textAfter) {
|
||||
if (state.token == htmlDispatch)
|
||||
return htmlMixedMode.indent(state.htmlState, textAfter);
|
||||
else
|
||||
return scriptingMode.indent(state.scriptState, textAfter);
|
||||
},
|
||||
|
||||
copyState: function(state) {
|
||||
return {
|
||||
token : state.token,
|
||||
htmlState : CodeMirror.copyState(htmlMixedMode, state.htmlState),
|
||||
scriptState : CodeMirror.copyState(scriptingMode, state.scriptState)
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
electricChars: "/{}:"
|
||||
}
|
||||
});
|
||||
|
||||
CodeMirror.defineMIME("application/x-ejs", { name: "htmlembedded", scriptingModeSpec:"javascript"});
|
||||
CodeMirror.defineMIME("application/x-aspx", { name: "htmlembedded", scriptingModeSpec:"text/x-csharp"});
|
||||
CodeMirror.defineMIME("application/x-jsp", { name: "htmlembedded", scriptingModeSpec:"text/x-java"});
|
||||
49
src/flask_debugtoolbar/static/codemirror/mode/htmlembedded/index.html
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CodeMirror: Html Embedded Scripts mode</title>
|
||||
<link rel="stylesheet" href="../../lib/codemirror.css">
|
||||
<script src="../../lib/codemirror.js"></script>
|
||||
<script src="../xml/xml.js"></script>
|
||||
<script src="../javascript/javascript.js"></script>
|
||||
<script src="../css/css.js"></script>
|
||||
<script src="../htmlmixed/htmlmixed.js"></script>
|
||||
<script src="htmlembedded.js"></script>
|
||||
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
|
||||
<link rel="stylesheet" href="../../doc/docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>CodeMirror: Html Embedded Scripts mode</h1>
|
||||
|
||||
<form><textarea id="code" name="code">
|
||||
<%
|
||||
function hello(who) {
|
||||
return "Hello " + who;
|
||||
}
|
||||
%>
|
||||
This is an example of EJS (embedded javascript)
|
||||
<p>The program says <%= hello("world") %>.</p>
|
||||
<script>
|
||||
alert("And here is some normal JS code"); // also colored
|
||||
</script>
|
||||
</textarea></form>
|
||||
|
||||
<script>
|
||||
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
||||
lineNumbers: true,
|
||||
matchBrackets: true,
|
||||
mode: "application/x-ejs",
|
||||
indentUnit: 4,
|
||||
indentWithTabs: true,
|
||||
enterMode: "keep",
|
||||
tabMode: "shift"
|
||||
});
|
||||
</script>
|
||||
|
||||
<p>Mode for html embedded scripts like JSP and ASP.NET. Depends on HtmlMixed which in turn depends on
|
||||
JavaScript, CSS and XML.<br />Other dependancies include those of the scriping language chosen.</p>
|
||||
|
||||
<p><strong>MIME types defined:</strong> <code>application/x-aspx</code> (ASP.NET),
|
||||
<code>application/x-ejs</code> (Embedded Javascript), <code>application/x-jsp</code> (JavaServer Pages)</p>
|
||||
</body>
|
||||
</html>
|
||||
83
src/flask_debugtoolbar/static/codemirror/mode/htmlmixed/htmlmixed.js
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
|
||||
var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true});
|
||||
var jsMode = CodeMirror.getMode(config, "javascript");
|
||||
var cssMode = CodeMirror.getMode(config, "css");
|
||||
|
||||
function html(stream, state) {
|
||||
var style = htmlMode.token(stream, state.htmlState);
|
||||
if (style == "tag" && stream.current() == ">" && state.htmlState.context) {
|
||||
if (/^script$/i.test(state.htmlState.context.tagName)) {
|
||||
state.token = javascript;
|
||||
state.localState = jsMode.startState(htmlMode.indent(state.htmlState, ""));
|
||||
state.mode = "javascript";
|
||||
}
|
||||
else if (/^style$/i.test(state.htmlState.context.tagName)) {
|
||||
state.token = css;
|
||||
state.localState = cssMode.startState(htmlMode.indent(state.htmlState, ""));
|
||||
state.mode = "css";
|
||||
}
|
||||
}
|
||||
return style;
|
||||
}
|
||||
function maybeBackup(stream, pat, style) {
|
||||
var cur = stream.current();
|
||||
var close = cur.search(pat);
|
||||
if (close > -1) stream.backUp(cur.length - close);
|
||||
return style;
|
||||
}
|
||||
function javascript(stream, state) {
|
||||
if (stream.match(/^<\/\s*script\s*>/i, false)) {
|
||||
state.token = html;
|
||||
state.localState = null;
|
||||
state.mode = "html";
|
||||
return html(stream, state);
|
||||
}
|
||||
return maybeBackup(stream, /<\/\s*script\s*>/,
|
||||
jsMode.token(stream, state.localState));
|
||||
}
|
||||
function css(stream, state) {
|
||||
if (stream.match(/^<\/\s*style\s*>/i, false)) {
|
||||
state.token = html;
|
||||
state.localState = null;
|
||||
state.mode = "html";
|
||||
return html(stream, state);
|
||||
}
|
||||
return maybeBackup(stream, /<\/\s*style\s*>/,
|
||||
cssMode.token(stream, state.localState));
|
||||
}
|
||||
|
||||
return {
|
||||
startState: function() {
|
||||
var state = htmlMode.startState();
|
||||
return {token: html, localState: null, mode: "html", htmlState: state};
|
||||
},
|
||||
|
||||
copyState: function(state) {
|
||||
if (state.localState)
|
||||
var local = CodeMirror.copyState(state.token == css ? cssMode : jsMode, state.localState);
|
||||
return {token: state.token, localState: local, mode: state.mode,
|
||||
htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
|
||||
},
|
||||
|
||||
token: function(stream, state) {
|
||||
return state.token(stream, state);
|
||||
},
|
||||
|
||||
indent: function(state, textAfter) {
|
||||
if (state.token == html || /^\s*<\//.test(textAfter))
|
||||
return htmlMode.indent(state.htmlState, textAfter);
|
||||
else if (state.token == javascript)
|
||||
return jsMode.indent(state.localState, textAfter);
|
||||
else
|
||||
return cssMode.indent(state.localState, textAfter);
|
||||
},
|
||||
|
||||
compareStates: function(a, b) {
|
||||
return htmlMode.compareStates(a.htmlState, b.htmlState);
|
||||
},
|
||||
|
||||
electricChars: "/{}:"
|
||||
}
|
||||
});
|
||||
|
||||
CodeMirror.defineMIME("text/html", "htmlmixed");
|
||||
51
src/flask_debugtoolbar/static/codemirror/mode/htmlmixed/index.html
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CodeMirror: HTML mixed mode</title>
|
||||
<link rel="stylesheet" href="../../lib/codemirror.css">
|
||||
<script src="../../lib/codemirror.js"></script>
|
||||
<script src="../xml/xml.js"></script>
|
||||
<script src="../javascript/javascript.js"></script>
|
||||
<script src="../css/css.js"></script>
|
||||
<script src="htmlmixed.js"></script>
|
||||
<link rel="stylesheet" href="../../doc/docs.css">
|
||||
<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>CodeMirror: HTML mixed mode</h1>
|
||||
<form><textarea id="code" name="code">
|
||||
<html style="color: green">
|
||||
<!-- this is a comment -->
|
||||
<head>
|
||||
<title>Mixed HTML Example</title>
|
||||
<style type="text/css">
|
||||
h1 {font-family: comic sans; color: #f0f;}
|
||||
div {background: yellow !important;}
|
||||
body {
|
||||
max-width: 50em;
|
||||
margin: 1em 2em 1em 5em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Mixed HTML Example</h1>
|
||||
<script>
|
||||
function jsFunc(arg1, arg2) {
|
||||
if (arg1 && arg2) document.body.innerHTML = "achoo";
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</textarea></form>
|
||||
<script>
|
||||
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: "text/html", tabMode: "indent"});
|
||||
</script>
|
||||
|
||||
<p>The HTML mixed mode depends on the XML, JavaScript, and CSS modes.</p>
|
||||
|
||||
<p><strong>MIME types defined:</strong> <code>text/html</code>
|
||||
(redefined, only takes effect if you load this parser after the
|
||||
XML parser).</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
77
src/flask_debugtoolbar/static/codemirror/mode/javascript/index.html
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CodeMirror: JavaScript mode</title>
|
||||
<link rel="stylesheet" href="../../lib/codemirror.css">
|
||||
<script src="../../lib/codemirror.js"></script>
|
||||
<script src="javascript.js"></script>
|
||||
<link rel="stylesheet" href="../../doc/docs.css">
|
||||
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>CodeMirror: JavaScript mode</h1>
|
||||
|
||||
<div><textarea id="code" name="code">
|
||||
// Demo code (the actual new parser character stream implementation)
|
||||
|
||||
function StringStream(string) {
|
||||
this.pos = 0;
|
||||
this.string = string;
|
||||
}
|
||||
|
||||
StringStream.prototype = {
|
||||
done: function() {return this.pos >= this.string.length;},
|
||||
peek: function() {return this.string.charAt(this.pos);},
|
||||
next: function() {
|
||||
if (this.pos < this.string.length)
|
||||
return this.string.charAt(this.pos++);
|
||||
},
|
||||
eat: function(match) {
|
||||
var ch = this.string.charAt(this.pos);
|
||||
if (typeof match == "string") var ok = ch == match;
|
||||
else var ok = ch && match.test ? match.test(ch) : match(ch);
|
||||
if (ok) {this.pos++; return ch;}
|
||||
},
|
||||
eatWhile: function(match) {
|
||||
var start = this.pos;
|
||||
while (this.eat(match));
|
||||
if (this.pos > start) return this.string.slice(start, this.pos);
|
||||
},
|
||||
backUp: function(n) {this.pos -= n;},
|
||||
column: function() {return this.pos;},
|
||||
eatSpace: function() {
|
||||
var start = this.pos;
|
||||
while (/\s/.test(this.string.charAt(this.pos))) this.pos++;
|
||||
return this.pos - start;
|
||||
},
|
||||
match: function(pattern, consume, caseInsensitive) {
|
||||
if (typeof pattern == "string") {
|
||||
function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
|
||||
if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
|
||||
if (consume !== false) this.pos += str.length;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
var match = this.string.slice(this.pos).match(pattern);
|
||||
if (match && consume !== false) this.pos += match[0].length;
|
||||
return match;
|
||||
}
|
||||
}
|
||||
};
|
||||
</textarea></div>
|
||||
|
||||
<script>
|
||||
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
||||
lineNumbers: true,
|
||||
matchBrackets: true
|
||||
});
|
||||
</script>
|
||||
|
||||
<p>JavaScript mode supports a single configuration
|
||||
option, <code>json</code>, which will set the mode to expect JSON
|
||||
data rather than a JavaScript program.</p>
|
||||
|
||||
<p><strong>MIME types defined:</strong> <code>text/javascript</code>, <code>application/json</code>.</p>
|
||||
</body>
|
||||
</html>
|
||||
360
src/flask_debugtoolbar/static/codemirror/mode/javascript/javascript.js
vendored
Normal file
@@ -0,0 +1,360 @@
|
||||
CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
||||
var indentUnit = config.indentUnit;
|
||||
var jsonMode = parserConfig.json;
|
||||
|
||||
// Tokenizer
|
||||
|
||||
var keywords = function(){
|
||||
function kw(type) {return {type: type, style: "keyword"};}
|
||||
var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
|
||||
var operator = kw("operator"), atom = {type: "atom", style: "atom"};
|
||||
return {
|
||||
"if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
|
||||
"return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
|
||||
"var": kw("var"), "const": kw("var"), "let": kw("var"),
|
||||
"function": kw("function"), "catch": kw("catch"),
|
||||
"for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
|
||||
"in": operator, "typeof": operator, "instanceof": operator,
|
||||
"true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
|
||||
};
|
||||
}();
|
||||
|
||||
var isOperatorChar = /[+\-*&%=<>!?|]/;
|
||||
|
||||
function chain(stream, state, f) {
|
||||
state.tokenize = f;
|
||||
return f(stream, state);
|
||||
}
|
||||
|
||||
function nextUntilUnescaped(stream, end) {
|
||||
var escaped = false, next;
|
||||
while ((next = stream.next()) != null) {
|
||||
if (next == end && !escaped)
|
||||
return false;
|
||||
escaped = !escaped && next == "\\";
|
||||
}
|
||||
return escaped;
|
||||
}
|
||||
|
||||
// Used as scratch variables to communicate multiple values without
|
||||
// consing up tons of objects.
|
||||
var type, content;
|
||||
function ret(tp, style, cont) {
|
||||
type = tp; content = cont;
|
||||
return style;
|
||||
}
|
||||
|
||||
function jsTokenBase(stream, state) {
|
||||
var ch = stream.next();
|
||||
if (ch == '"' || ch == "'")
|
||||
return chain(stream, state, jsTokenString(ch));
|
||||
else if (/[\[\]{}\(\),;\:\.]/.test(ch))
|
||||
return ret(ch);
|
||||
else if (ch == "0" && stream.eat(/x/i)) {
|
||||
stream.eatWhile(/[\da-f]/i);
|
||||
return ret("number", "number");
|
||||
}
|
||||
else if (/\d/.test(ch)) {
|
||||
stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
|
||||
return ret("number", "number");
|
||||
}
|
||||
else if (ch == "/") {
|
||||
if (stream.eat("*")) {
|
||||
return chain(stream, state, jsTokenComment);
|
||||
}
|
||||
else if (stream.eat("/")) {
|
||||
stream.skipToEnd();
|
||||
return ret("comment", "comment");
|
||||
}
|
||||
else if (state.reAllowed) {
|
||||
nextUntilUnescaped(stream, "/");
|
||||
stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
|
||||
return ret("regexp", "string-2");
|
||||
}
|
||||
else {
|
||||
stream.eatWhile(isOperatorChar);
|
||||
return ret("operator", null, stream.current());
|
||||
}
|
||||
}
|
||||
else if (ch == "#") {
|
||||
stream.skipToEnd();
|
||||
return ret("error", "error");
|
||||
}
|
||||
else if (isOperatorChar.test(ch)) {
|
||||
stream.eatWhile(isOperatorChar);
|
||||
return ret("operator", null, stream.current());
|
||||
}
|
||||
else {
|
||||
stream.eatWhile(/[\w\$_]/);
|
||||
var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
|
||||
return (known && state.kwAllowed) ? ret(known.type, known.style, word) :
|
||||
ret("variable", "variable", word);
|
||||
}
|
||||
}
|
||||
|
||||
function jsTokenString(quote) {
|
||||
return function(stream, state) {
|
||||
if (!nextUntilUnescaped(stream, quote))
|
||||
state.tokenize = jsTokenBase;
|
||||
return ret("string", "string");
|
||||
};
|
||||
}
|
||||
|
||||
function jsTokenComment(stream, state) {
|
||||
var maybeEnd = false, ch;
|
||||
while (ch = stream.next()) {
|
||||
if (ch == "/" && maybeEnd) {
|
||||
state.tokenize = jsTokenBase;
|
||||
break;
|
||||
}
|
||||
maybeEnd = (ch == "*");
|
||||
}
|
||||
return ret("comment", "comment");
|
||||
}
|
||||
|
||||
// Parser
|
||||
|
||||
var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true};
|
||||
|
||||
function JSLexical(indented, column, type, align, prev, info) {
|
||||
this.indented = indented;
|
||||
this.column = column;
|
||||
this.type = type;
|
||||
this.prev = prev;
|
||||
this.info = info;
|
||||
if (align != null) this.align = align;
|
||||
}
|
||||
|
||||
function inScope(state, varname) {
|
||||
for (var v = state.localVars; v; v = v.next)
|
||||
if (v.name == varname) return true;
|
||||
}
|
||||
|
||||
function parseJS(state, style, type, content, stream) {
|
||||
var cc = state.cc;
|
||||
// Communicate our context to the combinators.
|
||||
// (Less wasteful than consing up a hundred closures on every call.)
|
||||
cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
|
||||
|
||||
if (!state.lexical.hasOwnProperty("align"))
|
||||
state.lexical.align = true;
|
||||
|
||||
while(true) {
|
||||
var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
|
||||
if (combinator(type, content)) {
|
||||
while(cc.length && cc[cc.length - 1].lex)
|
||||
cc.pop()();
|
||||
if (cx.marked) return cx.marked;
|
||||
if (type == "variable" && inScope(state, content)) return "variable-2";
|
||||
return style;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Combinator utils
|
||||
|
||||
var cx = {state: null, column: null, marked: null, cc: null};
|
||||
function pass() {
|
||||
for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
|
||||
}
|
||||
function cont() {
|
||||
pass.apply(null, arguments);
|
||||
return true;
|
||||
}
|
||||
function register(varname) {
|
||||
var state = cx.state;
|
||||
if (state.context) {
|
||||
cx.marked = "def";
|
||||
for (var v = state.localVars; v; v = v.next)
|
||||
if (v.name == varname) return;
|
||||
state.localVars = {name: varname, next: state.localVars};
|
||||
}
|
||||
}
|
||||
|
||||
// Combinators
|
||||
|
||||
var defaultVars = {name: "this", next: {name: "arguments"}};
|
||||
function pushcontext() {
|
||||
if (!cx.state.context) cx.state.localVars = defaultVars;
|
||||
cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
|
||||
}
|
||||
function popcontext() {
|
||||
cx.state.localVars = cx.state.context.vars;
|
||||
cx.state.context = cx.state.context.prev;
|
||||
}
|
||||
function pushlex(type, info) {
|
||||
var result = function() {
|
||||
var state = cx.state;
|
||||
state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info)
|
||||
};
|
||||
result.lex = true;
|
||||
return result;
|
||||
}
|
||||
function poplex() {
|
||||
var state = cx.state;
|
||||
if (state.lexical.prev) {
|
||||
if (state.lexical.type == ")")
|
||||
state.indented = state.lexical.indented;
|
||||
state.lexical = state.lexical.prev;
|
||||
}
|
||||
}
|
||||
poplex.lex = true;
|
||||
|
||||
function expect(wanted) {
|
||||
return function expecting(type) {
|
||||
if (type == wanted) return cont();
|
||||
else if (wanted == ";") return pass();
|
||||
else return cont(arguments.callee);
|
||||
};
|
||||
}
|
||||
|
||||
function statement(type) {
|
||||
if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
|
||||
if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
|
||||
if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
|
||||
if (type == "{") return cont(pushlex("}"), block, poplex);
|
||||
if (type == ";") return cont();
|
||||
if (type == "function") return cont(functiondef);
|
||||
if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
|
||||
poplex, statement, poplex);
|
||||
if (type == "variable") return cont(pushlex("stat"), maybelabel);
|
||||
if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
|
||||
block, poplex, poplex);
|
||||
if (type == "case") return cont(expression, expect(":"));
|
||||
if (type == "default") return cont(expect(":"));
|
||||
if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
|
||||
statement, poplex, popcontext);
|
||||
return pass(pushlex("stat"), expression, expect(";"), poplex);
|
||||
}
|
||||
function expression(type) {
|
||||
if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
|
||||
if (type == "function") return cont(functiondef);
|
||||
if (type == "keyword c") return cont(maybeexpression);
|
||||
if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeoperator);
|
||||
if (type == "operator") return cont(expression);
|
||||
if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
|
||||
if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
|
||||
return cont();
|
||||
}
|
||||
function maybeexpression(type) {
|
||||
if (type.match(/[;\}\)\],]/)) return pass();
|
||||
return pass(expression);
|
||||
}
|
||||
|
||||
function maybeoperator(type, value) {
|
||||
if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator);
|
||||
if (type == "operator") return cont(expression);
|
||||
if (type == ";") return;
|
||||
if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
|
||||
if (type == ".") return cont(property, maybeoperator);
|
||||
if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator);
|
||||
}
|
||||
function maybelabel(type) {
|
||||
if (type == ":") return cont(poplex, statement);
|
||||
return pass(maybeoperator, expect(";"), poplex);
|
||||
}
|
||||
function property(type) {
|
||||
if (type == "variable") {cx.marked = "property"; return cont();}
|
||||
}
|
||||
function objprop(type) {
|
||||
if (type == "variable") cx.marked = "property";
|
||||
if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression);
|
||||
}
|
||||
function commasep(what, end) {
|
||||
function proceed(type) {
|
||||
if (type == ",") return cont(what, proceed);
|
||||
if (type == end) return cont();
|
||||
return cont(expect(end));
|
||||
}
|
||||
return function commaSeparated(type) {
|
||||
if (type == end) return cont();
|
||||
else return pass(what, proceed);
|
||||
};
|
||||
}
|
||||
function block(type) {
|
||||
if (type == "}") return cont();
|
||||
return pass(statement, block);
|
||||
}
|
||||
function vardef1(type, value) {
|
||||
if (type == "variable"){register(value); return cont(vardef2);}
|
||||
return cont();
|
||||
}
|
||||
function vardef2(type, value) {
|
||||
if (value == "=") return cont(expression, vardef2);
|
||||
if (type == ",") return cont(vardef1);
|
||||
}
|
||||
function forspec1(type) {
|
||||
if (type == "var") return cont(vardef1, forspec2);
|
||||
if (type == ";") return pass(forspec2);
|
||||
if (type == "variable") return cont(formaybein);
|
||||
return pass(forspec2);
|
||||
}
|
||||
function formaybein(type, value) {
|
||||
if (value == "in") return cont(expression);
|
||||
return cont(maybeoperator, forspec2);
|
||||
}
|
||||
function forspec2(type, value) {
|
||||
if (type == ";") return cont(forspec3);
|
||||
if (value == "in") return cont(expression);
|
||||
return cont(expression, expect(";"), forspec3);
|
||||
}
|
||||
function forspec3(type) {
|
||||
if (type != ")") cont(expression);
|
||||
}
|
||||
function functiondef(type, value) {
|
||||
if (type == "variable") {register(value); return cont(functiondef);}
|
||||
if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
|
||||
}
|
||||
function funarg(type, value) {
|
||||
if (type == "variable") {register(value); return cont();}
|
||||
}
|
||||
|
||||
// Interface
|
||||
|
||||
return {
|
||||
startState: function(basecolumn) {
|
||||
return {
|
||||
tokenize: jsTokenBase,
|
||||
reAllowed: true,
|
||||
kwAllowed: true,
|
||||
cc: [],
|
||||
lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
|
||||
localVars: parserConfig.localVars,
|
||||
context: parserConfig.localVars && {vars: parserConfig.localVars},
|
||||
indented: 0
|
||||
};
|
||||
},
|
||||
|
||||
token: function(stream, state) {
|
||||
if (stream.sol()) {
|
||||
if (!state.lexical.hasOwnProperty("align"))
|
||||
state.lexical.align = false;
|
||||
state.indented = stream.indentation();
|
||||
}
|
||||
if (stream.eatSpace()) return null;
|
||||
var style = state.tokenize(stream, state);
|
||||
if (type == "comment") return style;
|
||||
state.reAllowed = !!(type == "operator" || type == "keyword c" || type.match(/^[\[{}\(,;:]$/));
|
||||
state.kwAllowed = type != '.';
|
||||
return parseJS(state, style, type, content, stream);
|
||||
},
|
||||
|
||||
indent: function(state, textAfter) {
|
||||
if (state.tokenize != jsTokenBase) return 0;
|
||||
var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical,
|
||||
type = lexical.type, closing = firstChar == type;
|
||||
if (type == "vardef") return lexical.indented + 4;
|
||||
else if (type == "form" && firstChar == "{") return lexical.indented;
|
||||
else if (type == "stat" || type == "form") return lexical.indented + indentUnit;
|
||||
else if (lexical.info == "switch" && !closing)
|
||||
return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
|
||||
else if (lexical.align) return lexical.column + (closing ? 0 : 1);
|
||||
else return lexical.indented + (closing ? 0 : indentUnit);
|
||||
},
|
||||
|
||||
electricChars: ":{}"
|
||||
};
|
||||
});
|
||||
|
||||
CodeMirror.defineMIME("text/javascript", "javascript");
|
||||
CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
|
||||