Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61c667f3cc |
@@ -1 +0,0 @@
|
||||
repo_token: eESbYiv4An6KEvjpmguDs4L7YkubXbqn1
|
||||
22
.gitignore
vendored
@@ -1,27 +1,7 @@
|
||||
*.pyc
|
||||
changelog.sh
|
||||
babel
|
||||
.DS_Store
|
||||
.coverage
|
||||
_build
|
||||
_static
|
||||
_images
|
||||
caravel/bin/caravelc
|
||||
env_py3
|
||||
.eggs
|
||||
build
|
||||
*.db
|
||||
tmp
|
||||
caravel_config.py
|
||||
panoramix_config.py
|
||||
local_config.py
|
||||
env
|
||||
dist
|
||||
caravel.egg-info/
|
||||
app.db
|
||||
*.bak
|
||||
|
||||
# Node.js, webpack artifacts
|
||||
*.entry.js
|
||||
*.js.map
|
||||
node_modules
|
||||
npm-debug.log
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
doc-warnings: yes
|
||||
test-warnings: no
|
||||
strictness: medium
|
||||
max-line-length: 90
|
||||
uses:
|
||||
- flask
|
||||
autodetect: yes
|
||||
pylint:
|
||||
disable:
|
||||
- cyclic-import
|
||||
- invalid-name
|
||||
- logging-format-interpolation
|
||||
options:
|
||||
docstring-min-length: 10
|
||||
pep8:
|
||||
full: true
|
||||
ignore-paths:
|
||||
- docs
|
||||
- caravel/migrations/env.py
|
||||
- caravel/ascii_art.py
|
||||
ignore-patterns:
|
||||
- ^example/doc_.*\.py$
|
||||
- (^|/)docs(/|$)
|
||||
23
.travis.yml
@@ -1,23 +0,0 @@
|
||||
language: python
|
||||
python:
|
||||
- "2.7"
|
||||
- "3.4"
|
||||
- "3.5"
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.wheelhouse/
|
||||
before_install:
|
||||
- npm install -g npm@'>=2.7.1'
|
||||
install:
|
||||
- pip wheel -w $HOME/.wheelhouse -f $HOME/.wheelhouse .
|
||||
- pip install --find-links=$HOME/.wheelhouse --no-index .
|
||||
- pip install -r dev-reqs.txt
|
||||
- cd caravel/assets
|
||||
- npm --version
|
||||
- npm install
|
||||
- npm run lint
|
||||
- npm run prod
|
||||
- cd $TRAVIS_BUILD_DIR
|
||||
script: bash run_tests.sh
|
||||
after_success:
|
||||
- coveralls
|
||||
279
CHANGELOG.md
@@ -1,279 +0,0 @@
|
||||
## Change Log
|
||||
|
||||
### 0.8.7 (2016/04/14 05:45 +00:00)
|
||||
- [e5fabf3](https://github.com/airbnb/caravel/commit/e5fabf39df1b0fadc8b3bac02e21592cd61c3196) Adjusting positioning in the examples (@mistercrunch)
|
||||
- [eb5a417](https://github.com/airbnb/caravel/commit/eb5a41728edc46dbcf837a78a7229a8129ed4dbf) Change the size of the column datasource_name in datasource creation … (#345) (@gyzmau)
|
||||
- [91a3594](https://github.com/airbnb/caravel/commit/91a3594be34c18ea9a7b008b5f3a42149295a5e3) Fixing the thumbs and the galery (#346) (@mistercrunch)
|
||||
- [52ebdc5](https://github.com/airbnb/caravel/commit/52ebdc50170a695b0490048ad02f3762bc187e97) add treemap (#344) (@andrewhn)
|
||||
- [5cadd67](https://github.com/airbnb/caravel/commit/5cadd6794cfb6c70f54e968b9e82b2c991f5ea5b) Allowing to specify schema for tables (#330) (@mistercrunch)
|
||||
- [0a94b36](https://github.com/airbnb/caravel/commit/0a94b36d3d371771561aabf5a3943379a0c5cfbb) Adding version subcommand to CLI (#329) (@mistercrunch)
|
||||
- [21b3a5f](https://github.com/airbnb/caravel/commit/21b3a5f1990a06e9e555c1b296503b10f2851fc4) [big_number*] fixing a few bugs (#342) (@mistercrunch)
|
||||
- [a76ccf4](https://github.com/airbnb/caravel/commit/a76ccf462de01ec8f4c2a7134ef0be9e76d998f1) [welcome page] shows the most recently updated items first (#343) (@mistercrunch)
|
||||
- [8fe5790](https://github.com/airbnb/caravel/commit/8fe5790ec35b00f2f86df8ac4ab7444601ddc539) Don't die on malformed json in the extras field. Give feedback to user (#338) (@deniszgonjanin)
|
||||
- [1e08b3e](https://github.com/airbnb/caravel/commit/1e08b3e8c589fc70d7cd4a14a5a9de49e2a93a45) add tooltips to sankey (#341) (@williaster)
|
||||
- [77828b6](https://github.com/airbnb/caravel/commit/77828b630ab0f151c0377b6d1304c39e1cfdf65a) [bugfix] druid granularity form field should be select2_freeform (@mistercrunch)
|
||||
- [af417b6](https://github.com/airbnb/caravel/commit/af417b6b58b4b4de4885d7d5ae16dd12626df401) Adding expression column description in the CRUD (@mistercrunch)
|
||||
- [3d1fc99](https://github.com/airbnb/caravel/commit/3d1fc99353557aa42c14e760e01774fd624d7805) Adding not in docs about connecting to dbs over SSL (@mistercrunch)
|
||||
- [1dd4165](https://github.com/airbnb/caravel/commit/1dd416570a3b4f5d431135459e9049e3eaf8f3a8) Json dumps iso date for Table view (#328) (@mistercrunch)
|
||||
- [54fb76e](https://github.com/airbnb/caravel/commit/54fb76e68066257120a5056ca09f9a48fef797ec) Getting the SQL view back in a working state (#327) (@mistercrunch)
|
||||
- [74975a1](https://github.com/airbnb/caravel/commit/74975a1606123c00f337b1b04b3594e962808275) Getting error messages to show up on the interactive debugger (@mistercrunch)
|
||||
- [6d0b576](https://github.com/airbnb/caravel/commit/6d0b5767b3a74b23bf248fcfc2cd270233af95a9) Include connection parameters in call to Test Connection for a new database (#326) (@deniszgonjanin)
|
||||
- [9ac979a](https://github.com/airbnb/caravel/commit/9ac979a336f0532d2323b3b37e1d1d2706e46216) Tweaks for the doc's gallery (@mistercrunch)
|
||||
- [f32ebee](https://github.com/airbnb/caravel/commit/f32ebeeb989fe5332350ea2eb3b2c6c518495603) Fixing the way tables overflow in dashboard view (#310) (@mistercrunch)
|
||||
- [807d686](https://github.com/airbnb/caravel/commit/807d68631273a5a9f0e9c460cdedf9da19370153) Showing thumbsnails when switching viz (#313) (@mistercrunch)
|
||||
- [5b82249](https://github.com/airbnb/caravel/commit/5b822492c884c9b7c366f7d02c685eb1b7efc976) Linting to 99% (#317) (@mistercrunch)
|
||||
- [c6da811](https://github.com/airbnb/caravel/commit/c6da8117b7d827f98984126d656470d93c9adc8f) Adding missing dep to dev-reqs.txt (@mistercrunch)
|
||||
- [a9af6b1](https://github.com/airbnb/caravel/commit/a9af6b1b9697ad2f5c8808555f0f83b6fb83a3dc) Fix for undefined inner_time_filter on missing granularity in models.SqlaTable (#309) (@antbell)
|
||||
- [fe045ad](https://github.com/airbnb/caravel/commit/fe045ad076d2c7d3810feb49dda84170e1595b6b) New viz: box plot (#312) (@andrewhn)
|
||||
- [f8e5d30](https://github.com/airbnb/caravel/commit/f8e5d30e2eb4ef7c7d3312dc57d45bafe4c390cc) Revert previous css commit (@mistercrunch)
|
||||
- [bfd1c87](https://github.com/airbnb/caravel/commit/bfd1c8711bf3de1218eb2f091c013b15d02a883c) Forcing widget overflow to be visible (@mistercrunch)
|
||||
- [542b66e](https://github.com/airbnb/caravel/commit/542b66ef6800a7256ea307445536b0f6b85992e6) Giving credit where credit is due (#307) (@mistercrunch)
|
||||
- [866e00d](https://github.com/airbnb/caravel/commit/866e00d78cf90a0022f9cfa21d098784e5d27c04) Detecting loops in Sankeys (#271) (@mistercrunch)
|
||||
- [a3dcb0f](https://github.com/airbnb/caravel/commit/a3dcb0f309bf8c8036ce844c750053e35244b437) closes #292 (#304) (@mistercrunch)
|
||||
- [25831f0](https://github.com/airbnb/caravel/commit/25831f00339b11f152b2f65904bb3fa9d9af05d0) Making prod web server run by default (@mistercrunch)
|
||||
- [b47ca78](https://github.com/airbnb/caravel/commit/b47ca785c7c068d6ba5985fb25efcd4c0e565a15) Preserve order of breakdowns in bar_dist (#302) (@amancevice)
|
||||
- [ef992b6](https://github.com/airbnb/caravel/commit/ef992b64491465455799cfe74210312273f7f55e) Fix week and month Time grain in MySQL (#297) (@prihoda)
|
||||
- [fdcedd0](https://github.com/airbnb/caravel/commit/fdcedd097f4e39173935923123eeb994f02dd469) Adding edit link to datasource in explore view (#303) (@mistercrunch)
|
||||
- [9a02c88](https://github.com/airbnb/caravel/commit/9a02c88afa56f1b1fc6515308e2400811409d8bd) Adding link to docs on navbar, re-indent (@mistercrunch)
|
||||
- [b2de935](https://github.com/airbnb/caravel/commit/b2de935411ea2f77d9a4172f17864c90fac70bb6) Add OS dependencies link in development env docs (#300) (@lerrua)
|
||||
- [ef64884](https://github.com/airbnb/caravel/commit/ef64884e532f2ff09a54cb4bc60a52e8534d95a6) [heatmap] fix bug to display this view properly in dashboards. fix bug to display their d3-tip tooltips in dashboards. don't show empty tooltips on heatmap. update logic for margins so they fit dynamically based on label size. (#301) (@williaster)
|
||||
- [0afa5d2](https://github.com/airbnb/caravel/commit/0afa5d2cbaf1f4e009d81fe9c117f28178f3cbba) Added FAQ and db dependencies to docs (@mistercrunch)
|
||||
- [eff0beb](https://github.com/airbnb/caravel/commit/eff0beb195b85f831d14e147a6dbecceb02dbc4e) Optimizing import statements for better readability and to avoid unused, excessive and duplicating imports in the project. (#294) (@sid88in)
|
||||
- [2972122](https://github.com/airbnb/caravel/commit/2972122f40780a5d0f56e5037311764fb7e46781) Adding link to Strata slides (@mistercrunch)
|
||||
|
||||
|
||||
### 0.8.6 (2016/04/07 21:26 +00:00)
|
||||
- [d933a21](https://github.com/airbnb/caravel/commit/d933a2121608de06946850792c059ce26be7b473) Prettyfying an image in the docs (@mistercrunch)
|
||||
- [c5dead4](https://github.com/airbnb/caravel/commit/c5dead4791b9d1444c0f42e4c34790160c63bbb6) v0.8.6 (@mistercrunch)
|
||||
- [59169bf](https://github.com/airbnb/caravel/commit/59169bfc96a5dd682f551a42def9623aaa1e0b4c) Merge pull request #212 from airbnb/big_number_total (@michellethomas)
|
||||
- [bcca840](https://github.com/airbnb/caravel/commit/bcca840f01547399a56c5273b006b2711d3df171) Adding from __future__ imports (#288) (@mistercrunch)
|
||||
- [90a3b9f](https://github.com/airbnb/caravel/commit/90a3b9f2c4ab2b8bf2888852bbacfd53afeaf85a) Update INTHEWILD.md (@mherr)
|
||||
- [a37e431](https://github.com/airbnb/caravel/commit/a37e4311508810f09e144bd7667a24e822e1e2c5) Adding _images to .gitignore (@mistercrunch)
|
||||
- [bf38c71](https://github.com/airbnb/caravel/commit/bf38c714a51ac3a32ccc4ba73e4868ae5b8a103e) Adding missing images (@mistercrunch)
|
||||
- [6b0b03e](https://github.com/airbnb/caravel/commit/6b0b03e0092bb90c783c2e67c898152bb01cb3e1) Fix localhost link in installation docs (@jules2689)
|
||||
- [8556b09](https://github.com/airbnb/caravel/commit/8556b098f9a152b2497938dbf1ca41742dbd3aff) Enable Time Grain Option for Redshift (@mistercrunch)
|
||||
- [d122b37](https://github.com/airbnb/caravel/commit/d122b37f5d182cd2bdbbf500ff7a1ac0b761d2e3) Add python-pip to the install docs (@jules2689)
|
||||
- [1756c27](https://github.com/airbnb/caravel/commit/1756c279302f4df08cf1242fe3fec570d548240b) Adding upgrade instructions to docs (@mistercrunch)
|
||||
- [7867267](https://github.com/airbnb/caravel/commit/786726760848e72abe9bb67d629ae805e3a171fa) SqlAlchemy -> SQLAlchemy in README.md (@AaronCritchley)
|
||||
- [92d5886](https://github.com/airbnb/caravel/commit/92d588694b82e18dbbd458144582ca59a8248d0b) Improving the Installation docs (@mistercrunch)
|
||||
- [d10eaec](https://github.com/airbnb/caravel/commit/d10eaeccc916f665e0538a799cd34be4c9983ac4) Adding a Gallery to the docs (@mistercrunch)
|
||||
- [c2bb49f](https://github.com/airbnb/caravel/commit/c2bb49fec567c72f9752f99db783225787d87eb6) Fix 4e6a06bad7a8_init.py migration script to work with Postgres (@greens231)
|
||||
- [062f2b8](https://github.com/airbnb/caravel/commit/062f2b81cf56e5c49453691fe800db2de8753e61) Datasource dropdown in Explore view (@mistercrunch)
|
||||
- [65e72d0](https://github.com/airbnb/caravel/commit/65e72d0d075d158c6f4a934e04bfa90ce29ed0ee) Csv download improvements (@andrewhn)
|
||||
- [3457276](https://github.com/airbnb/caravel/commit/345727635e23f71c39e2afeec4e07115b941730e) Adding y_axis_format to DistributionBarViz (@mistercrunch)
|
||||
- [c2baa53](https://github.com/airbnb/caravel/commit/c2baa53b060cda4352582d238f53369e3f7773d0) bugfix datatables move to new package (@mistercrunch)
|
||||
- [3175882](https://github.com/airbnb/caravel/commit/31758827aeeea65063f4a92d7a86a0ce993dde25) + button on Slice list view redirects to Table view with alert (@mistercrunch)
|
||||
- [81de51b](https://github.com/airbnb/caravel/commit/81de51bf6ff772c3f44c2022e1f170c506c3d5b7) Minor tweaks (@mistercrunch)
|
||||
- [0d1f27d](https://github.com/airbnb/caravel/commit/0d1f27dbc1a622fb0e40cb0fa872340349477d03) add postgres grains (@andrewhn)
|
||||
- [c728288](https://github.com/airbnb/caravel/commit/c7282882d564889d90e334cefddc8b42bbb22dd9) Fixing the pagination display on welcome (@mistercrunch)
|
||||
- [f9d04e8](https://github.com/airbnb/caravel/commit/f9d04e8a7269455e21a0ad13fc235b5f3f01777e) Fixed refresh_datasource redirect (@mistercrunch)
|
||||
- [bf2e804](https://github.com/airbnb/caravel/commit/bf2e8043313cf31ef0a1f79df4902aef8c5f0b12) Removed trailing coma in Database.extra default (@mistercrunch)
|
||||
- [c349b0a](https://github.com/airbnb/caravel/commit/c349b0a1c1059863cf27d3c0651986ba1330a045) Fixed link url in docs (@mistercrunch)
|
||||
- [4d640b5](https://github.com/airbnb/caravel/commit/4d640b5a3d159e6d184fb6cdc6e605c356bf35d5) [fix] panel overflowing on welcome page (@mistercrunch)
|
||||
- [380c3f0](https://github.com/airbnb/caravel/commit/380c3f0c7559a8137570a525b72b1488fe0854bb) Using boostrap panels for form fieldsets in explore view (@mistercrunch)
|
||||
- [e3e8202](https://github.com/airbnb/caravel/commit/e3e8202c98ba925f2677f12d3c068635d2fb026a) clear element before redrawing sankey (@andrewhn)
|
||||
- [8898444](https://github.com/airbnb/caravel/commit/889844407f1fd90821f108cb9c505dd7ff5c86e1) Adding extra options to deeper configure sqlalchemy (@mistercrunch)
|
||||
- [f1830c3](https://github.com/airbnb/caravel/commit/f1830c36cf4cf97bccadbd9a9afa99f12a67d115) A better welcome page (@mistercrunch)
|
||||
- [92f73b6](https://github.com/airbnb/caravel/commit/92f73b67ca1c6f486961962c796128f38320c3b8) Move window.alert() calls to bootstrap modals. Also log errors to console. (@williaster)
|
||||
- [9c1af66](https://github.com/airbnb/caravel/commit/9c1af66ba425ce4d353c722af39a6b6cfdfd08ff) Fix ignored SQL where clauses (@skje)
|
||||
- [2b31ab4](https://github.com/airbnb/caravel/commit/2b31ab498b4187a000f3c5a480831d59f8486e73) [hotfix] fixing json endpoint (@mistercrunch)
|
||||
- [034fd07](https://github.com/airbnb/caravel/commit/034fd077e1215366c59742be5475abdc3bad1292) Doc formating fix (@mistercrunch)
|
||||
- [ca44432](https://github.com/airbnb/caravel/commit/ca4443247e0e97d0466efa5876286967b490805e) Prettyfying the Caravel on README (@mistercrunch)
|
||||
- [6f96252](https://github.com/airbnb/caravel/commit/6f96252e45f1e318902833c9622ab3d6e57555fd) A logo on the navbar (@mistercrunch)
|
||||
- [0b93fd3](https://github.com/airbnb/caravel/commit/0b93fd373d2a9a1b5a6b844de112351c4a825218) [hotfix] hashing unicode in py3 (@mistercrunch)
|
||||
- [c3789d5](https://github.com/airbnb/caravel/commit/c3789d53b4b071e6f076a9dbe7137d9b0a565b52) Removing duplicate get_table in fetch_metadata (@Jimexist)
|
||||
- [aec3c0b](https://github.com/airbnb/caravel/commit/aec3c0b3580da23a767c0a83608170f8a89ce704) Fixing bug when datasource has been deleted (@mistercrunch)
|
||||
- [ef45c20](https://github.com/airbnb/caravel/commit/ef45c205585f101b25f1610e9c1327b783518a9f) Hash cache keys to avoid too keys being too long. Resolves #240 (@karel1980)
|
||||
- [10ab678](https://github.com/airbnb/caravel/commit/10ab678fc6e2257bb2a71220f8714a110fc8ba83) Finishing up the tutorial (@mistercrunch)
|
||||
- [87fb40a](https://github.com/airbnb/caravel/commit/87fb40ae2655a28c32197a01541a57f7d4bc3634) Bumping version of npm up (@mistercrunch)
|
||||
- [d245fb9](https://github.com/airbnb/caravel/commit/d245fb91b509810232118bd3f6ddf371687038c3) travis tweaks (@mistercrunch)
|
||||
- [c60032a](https://github.com/airbnb/caravel/commit/c60032accefba2fbf64814a8e680368f990fc16e) Pillow with a capital P (@mistercrunch)
|
||||
- [d2f5190](https://github.com/airbnb/caravel/commit/d2f51900f1853e6c40152407651f3baf298e4cde) Adding a tutorial (@mistercrunch)
|
||||
- [93405dc](https://github.com/airbnb/caravel/commit/93405dc23a3056da63bbf4d15ec33e6576f7f20a) Clarify SQLALCHEMY_DATABASE_URI in the docs (@mistercrunch)
|
||||
- [dafdb51](https://github.com/airbnb/caravel/commit/dafdb51f350778ba26684c3ccbc91c44a444897b) Adding Caravel Docker link to README (@mistercrunch)
|
||||
- [2d0fdf7](https://github.com/airbnb/caravel/commit/2d0fdf7e59ab88651a914835a685cfee61ad68aa) Adding a CHANGELOG.md (@mistercrunch)
|
||||
|
||||
|
||||
### 0.8.5 (2016/04/01 20:30 +00:00)
|
||||
- [#234](https://github.com/airbnb/caravel/pull/234) Pin pandas, remove numpy (@mistercrunch)
|
||||
- [#229](https://github.com/airbnb/caravel/pull/229) Remove "requirements.txt" mention from README (@jmcomets)
|
||||
- [#225](https://github.com/airbnb/caravel/pull/225) remove power units from sankey diagram (@williaster)
|
||||
- [#219](https://github.com/airbnb/caravel/pull/219) Add 'Percent of previous' to sunburst vis. Appease npm warnings for data tables and d3.layout.cloud (@williaster)
|
||||
- [#224](https://github.com/airbnb/caravel/pull/224) Fixing minor typos in the readme (@cyrusstoller)
|
||||
- [#214](https://github.com/airbnb/caravel/pull/214) Fix an installation bug. (@kim-pham-airbnb)
|
||||
- [#218](https://github.com/airbnb/caravel/pull/218) Redirecting URL from previous names to caravel (@mistercrunch)
|
||||
- [#223](https://github.com/airbnb/caravel/pull/223) Fixed typo in README (@thebucknerlife)
|
||||
- [#222](https://github.com/airbnb/caravel/pull/222) remove duplicate Druid.io section in README.md (@brchristian)
|
||||
- [#213](https://github.com/airbnb/caravel/pull/213) Fix a bug when loading DruidDatasource. (@kim-pham-airbnb)
|
||||
- [#204](https://github.com/airbnb/caravel/pull/204) Fixing the order and coverage report for the unit tests (@mistercrunch)
|
||||
- [#209](https://github.com/airbnb/caravel/pull/209) Fresh screenshots (@mistercrunch)
|
||||
- [#206](https://github.com/airbnb/caravel/pull/206) Caravel (@mistercrunch)
|
||||
- [#205](https://github.com/airbnb/caravel/pull/205) fix sunburst error. add `less` to package.json (@williaster)
|
||||
- [#203](https://github.com/airbnb/caravel/pull/203) Fixing mysql install (@mistercrunch)
|
||||
- [#202](https://github.com/airbnb/caravel/pull/202) Using setup.py nosetests to run tests (@mistercrunch)
|
||||
- [#199](https://github.com/airbnb/caravel/pull/199) Fix a few minor bugs (@mistercrunch)
|
||||
- [#200](https://github.com/airbnb/caravel/pull/200) Add a sankey example (@mistercrunch)
|
||||
- [#192](https://github.com/airbnb/caravel/pull/192) Fix Druid metadata refresh. (@kim-pham-airbnb)
|
||||
- [#198](https://github.com/airbnb/caravel/pull/198) A welcome page (@mistercrunch)
|
||||
- [#197](https://github.com/airbnb/caravel/pull/197) Adding a DRUID_IS_ACTIVE flag and changing nav bar (@NiharikaRay)
|
||||
- [#196](https://github.com/airbnb/caravel/pull/196) Fixing issues around fk nullable=False on audit fields (@mistercrunch)
|
||||
|
||||
### 0.8.4 (2016/03/24 05:26 +00:00)
|
||||
- [#193](https://github.com/airbnb/caravel/pull/193) Adding favorites for Slices and Dashboards (@mistercrunch)
|
||||
|
||||
### 0.8.2 (2016/03/23 20:43 +00:00)
|
||||
- [#188](https://github.com/airbnb/caravel/pull/188) Introducing a caching layer! (@mistercrunch)
|
||||
|
||||
### 0.8.1 (2016/03/21 23:41 +00:00)
|
||||
- [#191](https://github.com/airbnb/caravel/pull/191) Add week ending and week start to grain (@airbnb)
|
||||
- [#190](https://github.com/airbnb/caravel/pull/190) Cranking up version numbers (@mistercrunch)
|
||||
- [#184](https://github.com/airbnb/caravel/pull/184) sunburst improvements (@williaster)
|
||||
- [#186](https://github.com/airbnb/caravel/pull/186) Adding docstrings ! (@mistercrunch)
|
||||
- [#181](https://github.com/airbnb/caravel/pull/181) Dynamic time granularity on any datetime column (@mistercrunch)
|
||||
- [#182](https://github.com/airbnb/caravel/pull/182) more css fixes (@williaster)
|
||||
- [#178](https://github.com/airbnb/caravel/pull/178) Allowing all extra fields in AuditMixin to be nullable (@mistercrunch)
|
||||
- [#175](https://github.com/airbnb/caravel/pull/175) refactor dashboard chart html, make several css improvements. (@williaster)
|
||||
|
||||
### 0.8.0 (2016/03/11 03:33 +00:00)
|
||||
- [#172](https://github.com/airbnb/caravel/pull/172) Fixing the python and js packaging (@mistercrunch)
|
||||
- [#171](https://github.com/airbnb/caravel/pull/171) Fixing multiple refresh bug in filter_box (@mistercrunch)
|
||||
- [#169](https://github.com/airbnb/caravel/pull/169) Fixing the look of select2 components (@mistercrunch)
|
||||
- [#168](https://github.com/airbnb/caravel/pull/168) Getting travis to build the npm related stuff (@mistercrunch)
|
||||
- [#166](https://github.com/airbnb/caravel/pull/166) make css theme customization easier by using less for bootstrap themes (@williaster)
|
||||
- [#163](https://github.com/airbnb/caravel/pull/163) Shipping with CSS templates out of the box (@mistercrunch)
|
||||
- [#164](https://github.com/airbnb/caravel/pull/164) Improving the docs (@mistercrunch)
|
||||
- [#165](https://github.com/airbnb/caravel/pull/165) Fixing window resize for explore and standalone (@mistercrunch)
|
||||
- [#161](https://github.com/airbnb/caravel/pull/161) Add linting to package.json, do all of the linting. (@williaster)
|
||||
- [#160](https://github.com/airbnb/caravel/pull/160) Fixing the dashed line when using time compare (@mistercrunch)
|
||||
- [#159](https://github.com/airbnb/caravel/pull/159) Fixing the standalone mode (@mistercrunch)
|
||||
- [#158](https://github.com/airbnb/caravel/pull/158) Refactor (@mistercrunch)
|
||||
- [#154](https://github.com/airbnb/caravel/pull/154) Digging into leap year bug and improvming tests (@mistercrunch)
|
||||
- [#157](https://github.com/airbnb/caravel/pull/157) add button to auto-copy short URLs in /explore page (@williaster)
|
||||
- [#149](https://github.com/airbnb/caravel/pull/149) Allowing to make certain widgets immune to filter (@mistercrunch)
|
||||
- [#151](https://github.com/airbnb/caravel/pull/151) Linting (@mistercrunch)
|
||||
- [#153](https://github.com/airbnb/caravel/pull/153) Improve README (@tay)
|
||||
- [#139](https://github.com/airbnb/caravel/pull/139) NPMification & Reactification (@williaster, @mistercrunch)
|
||||
- [#147](https://github.com/airbnb/caravel/pull/147) Tackling Featured Datasets (@mistercrunch)
|
||||
- [#148](https://github.com/airbnb/caravel/pull/148) Fix typo (@tay)
|
||||
- [#145](https://github.com/airbnb/caravel/pull/145) Moving files around ... yay! (@mistercrunch)
|
||||
- [#142](https://github.com/airbnb/caravel/pull/142) A few cosmetic fixes (nvd3 tooltips, buttons, tables) (@mistercrunch)
|
||||
- [#141](https://github.com/airbnb/caravel/pull/141) A simple base template for npm (@mistercrunch)
|
||||
- [#140](https://github.com/airbnb/caravel/pull/140) use the latest segment to extract metadata (@dayzzz)
|
||||
- [#136](https://github.com/airbnb/caravel/pull/136) Improved the bar char to allow for dimensional breakdowns (@mistercrunch)
|
||||
- [#134](https://github.com/airbnb/caravel/pull/134) Fixing the roles auto maintenance (@mistercrunch)
|
||||
- [#132](https://github.com/airbnb/caravel/pull/132) [nvd3] fixing the legend toggle bug (@mistercrunch)
|
||||
- [#131](https://github.com/airbnb/caravel/pull/131) More tests using doctests! (@mistercrunch)
|
||||
- [#130](https://github.com/airbnb/caravel/pull/130) Logging more (@mistercrunch)
|
||||
- [#129](https://github.com/airbnb/caravel/pull/129) Renaming Classes related to Druid (@mistercrunch)
|
||||
- [#127](https://github.com/airbnb/caravel/pull/127) SQL editor, eventually will be tied to a flow to create views (@mistercrunch)
|
||||
- [#128](https://github.com/airbnb/caravel/pull/128) Allowing definition of css templates (@mistercrunch)
|
||||
- [#126](https://github.com/airbnb/caravel/pull/126) New viz: Heatmap! (@mistercrunch)
|
||||
- [#125](https://github.com/airbnb/caravel/pull/125) Consistent colors rendered client side (@mistercrunch)
|
||||
- [#124](https://github.com/airbnb/caravel/pull/124) A more cohesive color strategy (@mistercrunch)
|
||||
|
||||
### 0.7.0 (2016/01/23 15:16 +00:00)
|
||||
- [#123](https://github.com/airbnb/caravel/pull/123) Adding a color factory (@mistercrunch)
|
||||
- [#122](https://github.com/airbnb/caravel/pull/122) Adding Parallel coordinates viz (@mistercrunch)
|
||||
- [#121](https://github.com/airbnb/caravel/pull/121) Iframe (@mistercrunch)
|
||||
- [#120](https://github.com/airbnb/caravel/pull/120) Slice information can be displayed in dashboard (@mistercrunch)
|
||||
- [#117](https://github.com/airbnb/caravel/pull/117) Doing some refactoring (@mistercrunch)
|
||||
- [#115](https://github.com/airbnb/caravel/pull/115) Providing options for Y axis number formating (@mistercrunch)
|
||||
- [#112](https://github.com/airbnb/caravel/pull/112) Adding an URL shortner (@mistercrunch)
|
||||
- [#113](https://github.com/airbnb/caravel/pull/113) Prettier checkboxes (@mistercrunch)
|
||||
- [#111](https://github.com/airbnb/caravel/pull/111) Loading another example amazing dash (@mistercrunch)
|
||||
- [#109](https://github.com/airbnb/caravel/pull/109) Getting browser history to work on the explore view (@mistercrunch)
|
||||
- [#108](https://github.com/airbnb/caravel/pull/108) pulling to the front on hover (@BradBaker)
|
||||
- [#104](https://github.com/airbnb/caravel/pull/104) simplifying tooltip code (@BradBaker)
|
||||
- [#105](https://github.com/airbnb/caravel/pull/105) adding stagger for all charts that have a date axis (@BradBaker)
|
||||
- [#102](https://github.com/airbnb/caravel/pull/102) Fix for 2-axis charts where it shrinks them a little bit (@bradmbaker, @BradBaker)
|
||||
- [#101](https://github.com/airbnb/caravel/pull/101) Add a Gitter chat badge to README.md (@gitter-badger)
|
||||
- [#100](https://github.com/airbnb/caravel/pull/100) Update tooltips with new classes (@bradmbaker)
|
||||
- [#99](https://github.com/airbnb/caravel/pull/99) Time resampling as in Pandas (@mistercrunch)
|
||||
- [#98](https://github.com/airbnb/caravel/pull/98) Change Scaling to Operate on SVG instead of Div (@bradmbaker)
|
||||
- [#96](https://github.com/airbnb/caravel/pull/96) Adding a filter box widget (@mistercrunch)
|
||||
- [#95](https://github.com/airbnb/caravel/pull/95) Working on docs (@mistercrunch)
|
||||
- [#94](https://github.com/airbnb/caravel/pull/94) Massive js refactor + Dashboard filters (@mistercrunch)
|
||||
- [#93](https://github.com/airbnb/caravel/pull/93) Controller (@mistercrunch)
|
||||
- [#92](https://github.com/airbnb/caravel/pull/92) Allowing not to group by on table view (@mistercrunch)
|
||||
- [#91](https://github.com/airbnb/caravel/pull/91) Exports (@mistercrunch)
|
||||
- [#90](https://github.com/airbnb/caravel/pull/90) A basic squeleton for the docs (@mistercrunch)
|
||||
- [#89](https://github.com/airbnb/caravel/pull/89) Featured datasets (@michellethomas)
|
||||
- [#87](https://github.com/airbnb/caravel/pull/87) fixing a few bugs with tool tip overflow (@BradBaker)
|
||||
- [#88](https://github.com/airbnb/caravel/pull/88) World Map viz with bubbles (@mistercrunch)
|
||||
- [#86](https://github.com/airbnb/caravel/pull/86) adjusting date formats (@BradBaker)
|
||||
- [#85](https://github.com/airbnb/caravel/pull/85) Adding sankey diagrams (@mistercrunch)
|
||||
- [#84](https://github.com/airbnb/caravel/pull/84) Adding directed force layout viz (@mistercrunch)
|
||||
- [#83](https://github.com/airbnb/caravel/pull/83) Big JS refactor (@mistercrunch)
|
||||
- [#82](https://github.com/airbnb/caravel/pull/82) letting tooltips in the dashboard overflow (@BradBaker)
|
||||
- [#81](https://github.com/airbnb/caravel/pull/81) Slightly better layout for explore page (@mistercrunch)
|
||||
- [#80](https://github.com/airbnb/caravel/pull/80) Checkboxes everywhere (@mistercrunch)
|
||||
- [#79](https://github.com/airbnb/caravel/pull/79) Cleanup around multiple select fields (@mistercrunch)
|
||||
|
||||
### 0.6.0 (2015/12/11 01:17 +00:00)
|
||||
- [#77](https://github.com/airbnb/caravel/pull/77) Better tooltips and more ways to integrate them easily (@mistercrunch)
|
||||
- [#76](https://github.com/airbnb/caravel/pull/76) Introducing form overrides for label and tooltips (@mistercrunch)
|
||||
- [#75](https://github.com/airbnb/caravel/pull/75) New viz: sunbursts (@mistercrunch)
|
||||
- [#74](https://github.com/airbnb/caravel/pull/74) Introducing fieldsets (@mistercrunch)
|
||||
- [#73](https://github.com/airbnb/caravel/pull/73) Airflowlike theme (@mistercrunch)
|
||||
- [#72](https://github.com/airbnb/caravel/pull/72) Logging slice and dash views (@mistercrunch)
|
||||
- [#70](https://github.com/airbnb/caravel/pull/70) Adding url slug support for dashboard model (@mistercrunch)
|
||||
- [#71](https://github.com/airbnb/caravel/pull/71) Add option to show minmax on x axis (@mistercrunch)
|
||||
- [#69](https://github.com/airbnb/caravel/pull/69) Allowing for [Save AS] and [Overwrite] (@mistercrunch)
|
||||
- [#68](https://github.com/airbnb/caravel/pull/68) Adding cumsum to rolling functions (@mistercrunch)
|
||||
- [#67](https://github.com/airbnb/caravel/pull/67) Fix debug mode calls get_json twice (@mistercrunch)
|
||||
- [#66](https://github.com/airbnb/caravel/pull/66) Adding a PivotTableViz (@mistercrunch)
|
||||
- [#65](https://github.com/airbnb/caravel/pull/65) Adding custom HAVING clause (@mistercrunch)
|
||||
- [#64](https://github.com/airbnb/caravel/pull/64) Preserving the ordering in selectmultiple (@mistercrunch)
|
||||
- [#63](https://github.com/airbnb/caravel/pull/63) Encrypting the passwords out of connection strings (@mistercrunch)
|
||||
- [#61](https://github.com/airbnb/caravel/pull/61) BetterBooleanField to fix html omitting non-checked <input> (@patrickleotardif)
|
||||
- [#60](https://github.com/airbnb/caravel/pull/60) Fix Markup Widget bug (@NiharikaRay)
|
||||
- [#59](https://github.com/airbnb/caravel/pull/59) Adding y-axis format option (@patrickleotardif)
|
||||
- [#58](https://github.com/airbnb/caravel/pull/58) Setting min_periods to 1 for rolling windows (@mistercrunch)
|
||||
- [#56](https://github.com/airbnb/caravel/pull/56) adding sort order of the slices on changed_on field (@mistercrunch)
|
||||
|
||||
### 0.5.2 (2015/10/24 01:06 +00:00)
|
||||
- [#53](https://github.com/airbnb/caravel/pull/53) Py3 (@mistercrunch)
|
||||
- [#51](https://github.com/airbnb/caravel/pull/51) Adding timezone offset as a datasource param (@mistercrunch)
|
||||
- [#52](https://github.com/airbnb/caravel/pull/52) Speed up travis builds with wheels (@mistercrunch)
|
||||
- [#48](https://github.com/airbnb/caravel/pull/48) Allowing to specify the gunicorn timeout in CLI and config (@mistercrunch)
|
||||
|
||||
### 0.5.0 (2015/10/13 01:09 +00:00)
|
||||
- [#46](https://github.com/airbnb/caravel/pull/46) Allowing to change the "Time Column" on SqlA (@mistercrunch)
|
||||
- [#45](https://github.com/airbnb/caravel/pull/45) Bootstrapping widgets from javascript initializer. (@akuhn)
|
||||
- [#43](https://github.com/airbnb/caravel/pull/43) Supporting arbitrary expressions (@mistercrunch)
|
||||
- [#42](https://github.com/airbnb/caravel/pull/42) Adding ability to style a dashboard with CSS (@mistercrunch)
|
||||
- [#41](https://github.com/airbnb/caravel/pull/41) Cleaning up the static folder (@mistercrunch)
|
||||
- [#35](https://github.com/airbnb/caravel/pull/35) A first draft on default security roles (@mistercrunch)
|
||||
- [#40](https://github.com/airbnb/caravel/pull/40) Introducing time comparison (@mistercrunch)
|
||||
- [#39](https://github.com/airbnb/caravel/pull/39) Adding interpolation choice for line charts (@mistercrunch)
|
||||
- [#38](https://github.com/airbnb/caravel/pull/38) Extract css rules and scripts into separate files. (@akuhn)
|
||||
- [#37](https://github.com/airbnb/caravel/pull/37) Viz type (@mistercrunch)
|
||||
- [#36](https://github.com/airbnb/caravel/pull/36) Extract widget javascript to separate files. (@akuhn)
|
||||
- [#34](https://github.com/airbnb/caravel/pull/34) Ripping out Highcharts. (@mistercrunch)
|
||||
|
||||
### 0.4.0 (2015/09/27 04:39 +00:00)
|
||||
- [#33](https://github.com/airbnb/caravel/pull/33) Adding nvd3 support (@mistercrunch)
|
||||
- [#32](https://github.com/airbnb/caravel/pull/32) Adding a foundation for unit tests (@mistercrunch)
|
||||
- [#31](https://github.com/airbnb/caravel/pull/31) Adding a button to test connections (@mistercrunch)
|
||||
- [#30](https://github.com/airbnb/caravel/pull/30) Word cloud widget! (@mistercrunch)
|
||||
- [#29](https://github.com/airbnb/caravel/pull/29) Adding support for markup (html/markdown) widgets (@mistercrunch)
|
||||
- [#28](https://github.com/airbnb/caravel/pull/28) Fix default Sqlite path. (@noddi)
|
||||
- [#27](https://github.com/airbnb/caravel/pull/27) More refactor and bugfixes (@mistercrunch)
|
||||
- [#26](https://github.com/airbnb/caravel/pull/26) Bugfix (@mistercrunch)
|
||||
- [#25](https://github.com/airbnb/caravel/pull/25) Adding basic dashboarding support! (@mistercrunch)
|
||||
- [#23](https://github.com/airbnb/caravel/pull/23) Custom WHERE clause for tables (not druid) + error handling refactor (@mistercrunch)
|
||||
- [#22](https://github.com/airbnb/caravel/pull/22) Form factory refactor (@mistercrunch)
|
||||
- [#20](https://github.com/airbnb/caravel/pull/20) add tzinfo config, useful when start druid without utc timezone (@wbchn)
|
||||
|
||||
### 0.2.1 (2015/09/05 22:08 +00:00)
|
||||
- [#19](https://github.com/airbnb/caravel/pull/19) Preparing pypi package (@mistercrunch)
|
||||
|
||||
### 0.2.0 (2015/09/05 20:43 +00:00)
|
||||
- [#16](https://github.com/airbnb/caravel/pull/16) Adding Bubble charts (@mistercrunch)
|
||||
- [#13](https://github.com/airbnb/caravel/pull/13) Now supporting SQL Multiple database (@mistercrunch)
|
||||
- [#12](https://github.com/airbnb/caravel/pull/12) Cosmetricks (@mistercrunch)
|
||||
- [#11](https://github.com/airbnb/caravel/pull/11) Fixing the ways metrics are autogenerated (@mistercrunch)
|
||||
- [#10](https://github.com/airbnb/caravel/pull/10) Now enabling multi-cluster, connection info managed in UI (@mistercrunch)
|
||||
- [#9](https://github.com/airbnb/caravel/pull/9) Multi delete action on datasources (@mistercrunch)
|
||||
- [#8](https://github.com/airbnb/caravel/pull/8) Preventing bad json from creating problems (@mistercrunch)
|
||||
- [#3](https://github.com/airbnb/caravel/pull/3) Implementing my own highcharts wrapper (@mistercrunch)
|
||||
122
CONTRIBUTING.md
@@ -9,7 +9,7 @@ You can contribute in many ways:
|
||||
|
||||
### Report Bugs
|
||||
|
||||
Report bugs through Github
|
||||
Report bugs through Gihub
|
||||
|
||||
If you are reporting a bug, please include:
|
||||
|
||||
@@ -28,10 +28,14 @@ open to whoever wants to implement it.
|
||||
Look through the GitHub issues for features. Anything tagged with
|
||||
"feature" is open to whoever wants to implement it.
|
||||
|
||||
We've created the operators, hooks, macros and executors we needed, but we
|
||||
made sure that this part of Airflow is extensible. New operators,
|
||||
hooks and operators are very welcomed!
|
||||
|
||||
### Documentation
|
||||
|
||||
Caravel could always use better documentation,
|
||||
whether as part of the official Caravel docs,
|
||||
Airflow could always use better documentation,
|
||||
whether as part of the official Airflow docs,
|
||||
in docstrings, `docs/*.rst` or even on the web as blog posts or
|
||||
articles.
|
||||
|
||||
@@ -47,136 +51,40 @@ If you are proposing a feature:
|
||||
- Remember that this is a volunteer-driven project, and that
|
||||
contributions are welcome :)
|
||||
|
||||
## Latest Documentation
|
||||
## Latests Documentation
|
||||
|
||||
[API Documentation](http://pythonhosted.com/caravel)
|
||||
|
||||
## Setting up a Python development environment
|
||||
|
||||
Check the [OS dependencies](http://airbnb.io/caravel/installation.html#os-dependencies) before follows these steps.
|
||||
|
||||
# fork the repo on github and then clone it
|
||||
# alternatively you may want to clone the main repo but that won't work
|
||||
# so well if you are planning on sending PRs
|
||||
# git clone git@github.com:airbnb/caravel.git
|
||||
|
||||
# [optional] setup a virtual env and activate it
|
||||
virtualenv env
|
||||
source env/bin/activate
|
||||
|
||||
# install for development
|
||||
python setup.py develop
|
||||
|
||||
# Create an admin user
|
||||
fabmanager create-admin --app caravel
|
||||
|
||||
# Initialize the database
|
||||
caravel db upgrade
|
||||
|
||||
# Create default roles and permissions
|
||||
caravel init
|
||||
|
||||
# Load some data to play with
|
||||
caravel load_examples
|
||||
|
||||
# start a dev web server
|
||||
caravel runserver -d
|
||||
|
||||
|
||||
## Setting up the node / npm javascript environment
|
||||
|
||||
`caravel/assets` contains all npm-managed, front end assets.
|
||||
Flask-Appbuilder itself comes bundled with jQuery and bootstrap.
|
||||
While these may be phased out over time, these packages are currently not
|
||||
managed with npm.
|
||||
|
||||
|
||||
### Using npm to generate bundled files
|
||||
|
||||
#### npm
|
||||
First, npm must be available in your environment. If it is not you can run the following commands
|
||||
(taken from [this source](https://gist.github.com/DanHerbert/9520689))
|
||||
```
|
||||
brew install node --without-npm
|
||||
echo prefix=~/.npm-packages >> ~/.npmrc
|
||||
curl -L https://www.npmjs.com/install.sh | sh
|
||||
```
|
||||
|
||||
The final step is to add
|
||||
`~/.node/bin` to your `PATH` so commands you install globally are usable.
|
||||
Add something like this to your `.bashrc` file.
|
||||
```
|
||||
export PATH="$HOME/.node/bin:$PATH"
|
||||
```
|
||||
|
||||
#### npm packages
|
||||
To install third party libraries defined in `package.json`, run the
|
||||
following within the `caravel/assets/` directory which will install them in a
|
||||
new `node_modules/` folder within `assets/`.
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
To parse and generate bundled files for caravel, run either of the
|
||||
following commands. The `dev` flag will keep the npm script running and
|
||||
re-run it upon any changes within the assets directory.
|
||||
|
||||
```
|
||||
# Compiles the production / optimized js & css
|
||||
npm run prod
|
||||
|
||||
# Start a web server that manages and updates your assets as you modify them
|
||||
npm run dev
|
||||
```
|
||||
|
||||
For every development session you will have to start a flask dev server
|
||||
as well as an npm watcher
|
||||
|
||||
```
|
||||
caravel runserver -d -p 8081
|
||||
npm run dev
|
||||
```
|
||||
[API Documentation](http://pythonhosted.com/airflow)
|
||||
|
||||
## Testing
|
||||
|
||||
Install development requirements:
|
||||
|
||||
pip install -r requirements.txt
|
||||
|
||||
Tests can then be run with:
|
||||
|
||||
./run_unit_tests.sh
|
||||
|
||||
Lint the project with:
|
||||
|
||||
# for python changes
|
||||
flake8 changes tests
|
||||
|
||||
# for javascript
|
||||
npm run lint
|
||||
|
||||
## API documentation
|
||||
|
||||
Generate the documentation with:
|
||||
|
||||
cd docs && ./build.sh
|
||||
|
||||
## CSS Themes
|
||||
As part of the npm build process, CSS for Caravel is compiled from ```Less```, a dynamic stylesheet language.
|
||||
|
||||
It's possible to customize or add your own theme to Caravel, either by overriding CSS rules or preferably
|
||||
by modifying the Less variables or files in ```assets/stylesheets/less/```.
|
||||
|
||||
The ```variables.less``` and ```bootswatch.less``` files that ship with Caravel are derived from
|
||||
[Bootswatch](https://bootswatch.com) and thus extend Bootstrap. Modify variables in these files directly, or
|
||||
swap them out entirely with the equivalent files from other Bootswatch (themes)[https://github.com/thomaspark/bootswatch.git]
|
||||
|
||||
## Pull Request Guidelines
|
||||
|
||||
Before you submit a pull request from your forked repo, check that it
|
||||
Before you submit a pull request from your forked repo, check that it
|
||||
meets these guidelines:
|
||||
|
||||
1. The pull request should include tests, either as doctests,
|
||||
unit tests, or both.
|
||||
2. If the pull request adds functionality, the docs should be updated
|
||||
as part of the same PR. Doc string are often sufficient, make
|
||||
as part of the same PR. Doc string are often sufficient, make
|
||||
sure to follow the sphinx compatible standards.
|
||||
3. The pull request should work for Python 2.6, 2.7, and ideally python 3.3.
|
||||
`from __future__ import ` will be required in every `.py` file soon.
|
||||
|
||||
11
INTHEWILD.md
@@ -1,11 +0,0 @@
|
||||
Please use [pull requests](https://github.com/airbnb/caravel/pull/new/master)
|
||||
to add your organization and/or project to this document!
|
||||
|
||||
Organizations
|
||||
----------
|
||||
- [Airbnb](https://github.com/airbnb)
|
||||
- [GfK Data Lab] (http://datalab.gfk.com)
|
||||
|
||||
Projects
|
||||
----------
|
||||
- None we know of yet
|
||||
@@ -1,8 +0,0 @@
|
||||
recursive-include caravel/templates *
|
||||
recursive-include caravel/static *
|
||||
recursive-exclude caravel/static/assets/node_modules *
|
||||
recursive-include caravel/static/assets/node_modules/font-awesome *
|
||||
recursive-exclude caravel/static/docs *
|
||||
recursive-exclude tests *
|
||||
recursive-include caravel/data *
|
||||
recursive-include caravel/migrations *
|
||||
193
README.md
@@ -1,112 +1,125 @@
|
||||
Caravel
|
||||
Panoramix
|
||||
=========
|
||||
<img src="http://i.imgur.com/H0Kyvyi.jpg" style="border-radius: 20px; box-shadow:5px 5px 5px gray;" alt="Caravel" width="500"/>
|
||||
|
||||
[](https://travis-ci.org/airbnb/caravel)
|
||||
[](https://badge.fury.io/py/caravel)
|
||||
[](https://coveralls.io/github/airbnb/caravel?branch=master)
|
||||
[](https://landscape.io/github/airbnb/caravel/master)
|
||||
[](https://requires.io/github/airbnb/caravel/requirements/?branch=master)
|
||||
[](https://gitter.im/airbnb/caravel?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](http://airbnb.io/caravel/)
|
||||
|
||||
Caravel is a data exploration platform designed to be visual, intuitive
|
||||
Panoramix is a data exploration platform designed to be visual, intuitive
|
||||
and interactive.
|
||||
|
||||
[this project used to be named **Panoramix**]
|
||||
|
||||
|
||||
Video - Introduction to Caravel
|
||||
---------------------------------
|
||||
[](http://www.youtube.com/watch?v=3Txm_nj_R7M)
|
||||
|
||||
Screenshots
|
||||
Buzz Phrases
|
||||
------------
|
||||

|
||||

|
||||
|
||||
Caravel
|
||||
---------
|
||||
Caravel's main goal is to make it easy to slice, dice and visualize data.
|
||||
It empowers users to perform **analytics at the speed of thought**.
|
||||
|
||||
Caravel provides:
|
||||
* A quick way to intuitively visualize datasets by allowing users to create
|
||||
and share interactive dashboards
|
||||
* A rich set of visualizations to analyze your data, as well as a flexible
|
||||
way to extend the capabilities
|
||||
* An extensible, high granularity security model allowing intricate rules
|
||||
on who can access which features, and integration with major
|
||||
authentication providers (database, OpenID, LDAP, OAuth & REMOTE_USER
|
||||
through Flask AppBuiler)
|
||||
* A simple semantic layer, allowing to control how data sources are
|
||||
displayed in the UI, by defining which fields should show up in
|
||||
which dropdown and which aggregation and function (metrics) are
|
||||
made available to the user
|
||||
* Deep integration with Druid allows for Caravel to stay blazing fast while
|
||||
slicing and dicing large, realtime datasets
|
||||
* Analytics at the speed of thought!
|
||||
* Instantaneous learning curve
|
||||
* Realtime analytics when querying [Druid.io](http://druid.io)
|
||||
* Extentsible to infinity
|
||||
|
||||

|
||||
|
||||
Database Support
|
||||
----------------
|
||||
|
||||
Caravel was originally designed on top of Druid.io, but quickly broadened
|
||||
its scope to support other databases through the use of SQLAlchemy, a Python
|
||||
ORM that is compatible with
|
||||
[most common databases](http://docs.sqlalchemy.org/en/rel_1_0/core/engines.html).
|
||||
Panoramix was originally designed on to of Druid.io, but quickly broadened
|
||||
its scope to support other databases through the use of SqlAlchemy, a Python
|
||||
ORM that is compatible with
|
||||
[many external databases](http://docs.sqlalchemy.org/en/rel_1_0/core/engines.html).
|
||||
|
||||
|
||||
What is Druid?
|
||||
What's Druid?
|
||||
-------------
|
||||
From their website at http://druid.io
|
||||
|
||||
*Druid is an open-source analytics data store designed for
|
||||
business intelligence (OLAP) queries on event data. Druid provides low
|
||||
latency (real-time) data ingestion, flexible data exploration,
|
||||
and fast data aggregation. Existing Druid deployments have scaled to
|
||||
trillions of events and petabytes of data. Druid is best used to
|
||||
*Druid is an open-source analytics data store designed for
|
||||
business intelligence (OLAP) queries on event data. Druid provides low
|
||||
latency (real-time) data ingestion, flexible data exploration,
|
||||
and fast data aggregation. Existing Druid deployments have scaled to
|
||||
trillions of events and petabytes of data. Druid is best used to
|
||||
power analytic dashboards and applications.*
|
||||
|
||||
Panoramix
|
||||
---------
|
||||
Panoramix's main goal is to make it easy to slice, dice and visualize data
|
||||
out of Druid. It empowers its user to perform **analytics
|
||||
at the speed of thought**.
|
||||
|
||||
Installation & Configuration
|
||||
----------------------------
|
||||
Panoramix started as a hackathon project at Airbnb in while running a POC
|
||||
(proof of concept) on using Druid.
|
||||
|
||||
[See in the documentation](http://airbnb.io/caravel/installation.html)
|
||||
Panoramix provides:
|
||||
* A way to query intuitively a Druid dataset, allowing for grouping, filtering
|
||||
limiting and defining a time granularity
|
||||
* Many charts and visualization to analyze your data, as well as a flexible
|
||||
way to extend the visualization capabilities
|
||||
* An extensible, high granularity security model allowing intricate rules
|
||||
on who can access which features, and integration with major
|
||||
authentication providers (through Flask AppBuiler)
|
||||
* A simple semantic layer, allowing to control how Druid datasources are
|
||||
displayed in the UI,
|
||||
by defining which fields should show up in which dropdown and which
|
||||
aggregation and function (metrics) are made available to the user
|
||||
|
||||
|
||||
More screenshots
|
||||
----------------
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
|
||||
Resources
|
||||
-------------
|
||||
* [Caravel Google Group](https://groups.google.com/forum/#!forum/airbnb_caravel)
|
||||
* [Gitter (live chat) Channel](https://gitter.im/airbnb/caravel)
|
||||
* [Docker image 1](https://hub.docker.com/r/kochalex/caravel/)
|
||||
[Docker image 2](https://hub.docker.com/r/amancevice/caravel/) (community contributed)
|
||||
* [Slides from Strata (March 2016)](https://drive.google.com/open?id=0B5PVE0gzO81oOVJkdF9aNkJMSmM)
|
||||
|
||||
|
||||
Tip of the Hat
|
||||
--------------
|
||||
|
||||
Caravel would not be possible without these great frameworks / libs
|
||||
|
||||
* Flask App Builder - Allowing us to focus on building the app quickly while
|
||||
getting the foundation for free
|
||||
* The Flask ecosystem - Simply amazing. So much Plug, easy play.
|
||||
* NVD3 - One of the best charting libraries out there
|
||||
* Much more, check out the `install_requires` section in the [setup.py](https://github.com/airbnb/caravel/blob/master/setup.py) file!
|
||||
|
||||
|
||||
Contributing
|
||||
Installation
|
||||
------------
|
||||
|
||||
Interested in contributing? Casual hacking? Check out [Contributing.MD](https://github.com/airbnb/caravel/blob/master/CONTRIBUTING.md)
|
||||
Follow these few simple steps to install Panoramix
|
||||
|
||||
```
|
||||
# Install panoramix
|
||||
pip install panoramix
|
||||
|
||||
# Create an admin user
|
||||
fabmanager create-admin --app panoramix
|
||||
|
||||
# Start the web server
|
||||
panoramix
|
||||
```
|
||||
|
||||
After installation, you should be able to point your browser to the right
|
||||
hostname:port [http://localhost:8088](http://localhost:8088), login using
|
||||
the credential you entered while creating the admin account, and navigate to
|
||||
`Menu -> Admin -> Refresh Metadata`. This action should bring in all of
|
||||
your datasources for Panoramix to be aware of, and they should show up in
|
||||
`Menu -> Datasources`, from where you can start playing with your data!
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
To configure your application, you need to create a file (module)
|
||||
`panoramix_config.py` and make sure it is in your PYTHONPATH. Here are some
|
||||
of the parameters you can copy / paste in that configuration module:
|
||||
|
||||
```
|
||||
#---------------------------------------------------------
|
||||
# Panoramix specifix config
|
||||
#---------------------------------------------------------
|
||||
ROW_LIMIT = 5000
|
||||
WEBSERVER_THREADS = 8
|
||||
|
||||
PANORAMIX_WEBSERVER_PORT = 8088
|
||||
#---------------------------------------------------------
|
||||
|
||||
#---------------------------------------------------------
|
||||
# Flask App Builder configuration
|
||||
#---------------------------------------------------------
|
||||
# Your App secret key
|
||||
SECRET_KEY = '\2\1thisismyscretkey\1\2\e\y\y\h'
|
||||
|
||||
# The SQLAlchemy connection string.
|
||||
SQLALCHEMY_DATABASE_URI = 'sqlite:///tmp/panoramix.db'
|
||||
|
||||
# Flask-WTF flag for CSRF
|
||||
CSRF_ENABLED = True
|
||||
|
||||
# Whether to run the web server in debug mode or not
|
||||
DEBUG = True
|
||||
```
|
||||
|
||||
This file also allows you to define configuration parameters used by
|
||||
Flask App Builder, the web framework used by Panoramix. Please consult
|
||||
the [Flask App Builder Documentation](http://flask-appbuilder.readthedocs.org/en/latest/config.html) for more information on how to configure Panoramix.
|
||||
|
||||
|
||||
* From the UI, enter the information about your clusters in the
|
||||
``Admin->Clusters`` menu by hitting the + sign.
|
||||
|
||||
* Once the Druid cluster connection information is entered, hit the
|
||||
``Admin->Refresh Metadata`` menu item to populate
|
||||
|
||||
* Navigate to your datasources
|
||||
|
||||
58
TODO.md
@@ -1,53 +1,7 @@
|
||||
# TODO
|
||||
List of TODO items for Caravel
|
||||
|
||||
## Important
|
||||
* **Getting proper JS testing:** unit tests on the Python side are pretty
|
||||
solid, but now we need a test suite for the JS part of the site,
|
||||
testing all the ajax-type calls
|
||||
* **Viz Plugins:** Allow people to define and share visualization plugins.
|
||||
ideally one would only need to drop in a set of files in a folder and
|
||||
Caravel would discover and expose the plugins
|
||||
|
||||
## Features
|
||||
* **Dashboard URL filters:** `{dash_url}#fltin__fieldname__value1,value2`
|
||||
* **Default slice:** choose a default slice for the dataset instead of
|
||||
default endpoint
|
||||
* **refresh freq**: specifying the refresh frequency of a dashboard and
|
||||
specific slices within it, some randomization would be nice
|
||||
* **Widget sets / chart grids:** a way to have all charts support making
|
||||
a series of charts and putting them in a grid. The same way that you
|
||||
can groupby for series, you could chart by. The form field set would be
|
||||
common and use a single field to "grid by", a limit number of chart as
|
||||
an N * N grid size.
|
||||
* **Advanced dashboard configuration:** currently you can define which
|
||||
slices in a dashboard are immune to filtering.
|
||||
* **Annotations layers:** allow for people to maintain data annotations,
|
||||
attached to a layer and time range. These layers can be added on top of
|
||||
some visualizations as annotations. An example of a layer might be
|
||||
"holidays" or "site outages", ...
|
||||
* **Slack integration** - TBD
|
||||
* **Sexy Viz Selector:** the visualization selector should be a nice large
|
||||
modal with nice thumbnails for each one of the viz
|
||||
* **Comments:** allow for people to comment on slices and dashes
|
||||
|
||||
|
||||
## Easy-ish fix
|
||||
* Build matrix to include mysql using tox
|
||||
* CREATE VIEW button from SQL editor
|
||||
* Test button for when editing SQL expression
|
||||
* Slider form element
|
||||
* datasource in explore mode could be a dropdown
|
||||
* [druid] Allow for post aggregations (ratios!)
|
||||
* in/notin filters autocomplete (druid)
|
||||
|
||||
## New viz
|
||||
* Maps that use geocodes
|
||||
* Time animated scatter plots
|
||||
* Horizon charts
|
||||
* Calendar heatmap
|
||||
* Chord diagram
|
||||
* ...
|
||||
|
||||
## Community
|
||||
* Turorial vids
|
||||
* in/notin filters autocomplete
|
||||
* DRUID: Allow for post aggregations (ratios!)
|
||||
* compare time ranges
|
||||
* csv export out of table view
|
||||
* Save / bookmark / url shortener
|
||||
* SQL: Find a way to manage granularity
|
||||
|
||||
@@ -29,7 +29,7 @@ script_location = migrations
|
||||
# are written from script.py.mako
|
||||
# output_encoding = utf-8
|
||||
|
||||
sqlalchemy.url = scheme://localhost/caravel
|
||||
sqlalchemy.url = scheme://localhost/panoramix
|
||||
|
||||
# Logging configuration
|
||||
[loggers]
|
||||
|
||||
3
babel/babel.cfg
Normal file
@@ -0,0 +1,3 @@
|
||||
[python: **.py]
|
||||
[jinja2: **/templates/**.html]
|
||||
encoding = utf-8
|
||||
1
babel/messages.pot
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
"""Package's main module!"""
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
from flask import Flask, redirect
|
||||
from flask.ext.appbuilder import SQLA, AppBuilder, IndexView
|
||||
from flask.ext.appbuilder.baseviews import expose
|
||||
from flask.ext.cache import Cache
|
||||
from flask.ext.migrate import Migrate
|
||||
|
||||
from caravel import version
|
||||
|
||||
VERSION = version.VERSION_STRING
|
||||
|
||||
APP_DIR = os.path.dirname(__file__)
|
||||
CONFIG_MODULE = os.environ.get('CARAVEL_CONFIG', 'caravel.config')
|
||||
|
||||
# Logging configuration
|
||||
logging.basicConfig(format='%(asctime)s:%(levelname)s:%(name)s:%(message)s')
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(CONFIG_MODULE)
|
||||
if not app.debug:
|
||||
# In production mode, add log handler to sys.stderr.
|
||||
app.logger.addHandler(logging.StreamHandler())
|
||||
app.logger.setLevel(logging.INFO)
|
||||
|
||||
db = SQLA(app)
|
||||
|
||||
cache = Cache(app, config=app.config.get('CACHE_CONFIG'))
|
||||
|
||||
migrate = Migrate(app, db, directory=APP_DIR + "/migrations")
|
||||
|
||||
|
||||
class MyIndexView(IndexView):
|
||||
@expose('/')
|
||||
def index(self):
|
||||
return redirect('/caravel/welcome')
|
||||
|
||||
appbuilder = AppBuilder(
|
||||
app, db.session,
|
||||
base_template='caravel/base.html',
|
||||
indexview=MyIndexView,
|
||||
security_manager_class=app.config.get("CUSTOM_SECURITY_MANAGER"))
|
||||
|
||||
sm = appbuilder.sm
|
||||
|
||||
get_session = appbuilder.get_session
|
||||
from caravel import config, views # noqa
|
||||
@@ -1,82 +0,0 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
|
||||
error = (
|
||||
"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM8OI++=~~~~~~=+?IODMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMMMMMMMMMMD$~~~~~~~~~~~~~~~~~~~~~~~=$MMMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMMMMMMMN8?:~~~~~~~~~~~~~~~~~~~~~~~~~~=+8NMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMMMMMO=~~~~~~~~~~~~~~~~~+I??~~~~~~~~~~~~~+DMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMMMNI~~~~~~~~~~~~~~~~~~IIIII=~~~~~~~~~~~~~~=NMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMM+=~~~~~~~~~~~~~~~~~~~=III+~~~~~~~~~~~~~~~~~?8MMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMM?~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+++=~~~~8MMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMI=~~~~~~~~~~~~~~~~~~~~~~~~~III?I~~~~~~~~,:++++++~~8MMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMN7~~~~~~~~~~~~~~~~==+=~~~~~~=IIIII~~~~~~:. ..:=++=~=MMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMO=~~~~~~~~~~~~~~~~+++=~~~~~~~~??I?I~~~~~~. ...,~~~~IMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMM~~~~~~~~~~~~~~~~~+++:,~~~~~~~~~~~?=~~~~~:. ..~~~~~OMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMM$=~~~~~~~~~~~~~~~=++:.. ..~~~~~~~~~~~~~~~~,. . . :~~~~~OMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMM~~~~~~~~~~~~~~~~+++,. .~~~~~~~~~~~~~~~.. .. . .~~~~~=OMMMMMMMMMM\n"+
|
||||
"MMMMMMMM?~~~~~~~~~~~~~~~=+~. .~~~~~~~~~~~~~~. ,MMMMM,=~~~~~~NMMMMMMMMM\n"+
|
||||
"MMMMMMMN~~~~~~~~~~~~~~~~~,. .,~~~~~~~~~~~~~.. ZMMM,+Z:~~~~~~$MMMMMMMMM\n"+
|
||||
"MMMMMM8?~~~~~~~~~~~~~~~~~.. ..~~~~~~~~~~~~~:. DMMM,+D~~~~~~~~IMMMMMMMM\n"+
|
||||
"MMMMMMI~~~~~~~~~~~~~~~~~~.. :MMMO~~~~~~~~~~~~~~~,.. ?MMMMMI~~~~~~~~~MMMMMMMM\n"+
|
||||
"MMMMMM=~~~~~~~~~~~~~~~~~~.. MMM+=M:~~~~~~~~~~~~~:. .:IM$~~~~~~~~~~~8MMMMMMM\n"+
|
||||
"MMMMMD~~~~~~~~~~~~~~~~~~~:. MMM:,M:~~~~~~~~~~~~~~~.......:~~~~~~~~~~$MMMMMMM\n"+
|
||||
"MMMMMI~~~~~~~~~~~~~~~~~~~~, MMMMMM~~~~~~~~~~~~~~~~~~,..:~~~~~~~~~~~~+MMMMMMM\n"+
|
||||
"MMMMD+~~~~~~~~~~~~~~~~~~~~~. $MMMM$~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~=MMMMMMM\n"+
|
||||
"MMMM8~~~~~~~~~~~~~~~~~~~~~~:. . .:~~~~~~,..:. .=~~~~~~~~~~~~~~~~~~~~MMMMMMM\n"+
|
||||
"MMMMO~~~~~~~~~~~~~~~~~~~~~~~:, .:~~~~~=8.. .+ . =8ZI~~~~~~~~~~~~~~~~=MMMMMMM\n"+
|
||||
"MMMMZ=~~~~~~~~~~~~~~~~~~~~~~~~:,,,:~~~~~~IZ8:. .O....888?~~~~~~~~~~~~~~~+MMMMMMM\n"+
|
||||
"MMMMO=~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~?888=...I~I88888O?~~~~~~~~~~~~~~7MMMMMMM\n"+
|
||||
"MMMMO~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Z888OO88888888888O?~~~~~~~~~~~~~OMMMMMMM\n"+
|
||||
"MMMMD+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~=8888888888888888888~~~~~~~~~~~~+MMMMMMMM\n"+
|
||||
"MMMMM7~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~?8888888888888888888?~~~~~~~~~~=$MMMMMMMM\n"+
|
||||
"MMMMMD~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~=$8888888888888888888O~~~~~~~~~~8MMMMMMMMM\n"+
|
||||
"MMMMMN=~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Z88888888888888888ZZ7=~~~~~~~~?MMMMMMMMMM\n"+
|
||||
"MMMMMMZ=~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Z88888888Z7I===~~~~~~~~~~~~~=OMMMMMMMMMMM\n"+
|
||||
"MMMMMMN$~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~=$88888O7?=~~~~~~~~~~~~~~~~~~OMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMM?~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~I8OZ+~~~~~~~~~~~~~~~~~~~~=DMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMM8=~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+$+=~~~~~~~~~~~~~~~~~~~~+MMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMD7~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~=$DMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMM?~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~=$OMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMD7=~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+ZMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMZ7=~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~78MMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMM8OI=~~~~~~~~~~~~~~~~~~~=+?ZDNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMMMMMNDZ7?++~=~==~+?IONMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\n"+
|
||||
"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM")
|
||||
|
||||
stacktrace="""
|
||||
-------------------------------------------------------------------------------------------------------
|
||||
=======================================================================================================
|
||||
-------------------------------------------------------------------------------------------------------
|
||||
___ ___ ___
|
||||
( ) ( ) ( )
|
||||
.--. | |_ .---. .--. | | ___ | |_ ___ .-. .---. .--. .--.
|
||||
/ _ \ ( __) / .-, \ / \ | | ( ) ( __) ( ) \ / .-, \ / \ / \\
|
||||
. .' `. ; | | (__) ; | | .-. ; | | ' / | | | ' .-. ; (__) ; | | .-. ; | .-. ;
|
||||
| ' | | | | ___ .'` | | |(___) | |,' / | | ___ | / (___) .'` | | |(___) | | | |
|
||||
_\_`.(___) | |( ) / .'| | | | | . '. | |( ) | | / .'| | | | | |/ |
|
||||
( ). '. | | | | | / | | | | ___ | | `. \ | | | | | | | / | | | | ___ | ' _.'
|
||||
| | `\ | | ' | | ; | ; | | '( ) | | \ \ | ' | | | | ; | ; | | '( ) | .'.-.
|
||||
; '._,' ' ' `-' ; ' `-' | ' `-' | | | \ . ' `-' ; | | ' `-' | ' `-' | ' `-' /
|
||||
'.___.' `.__. `.__.'_. `.__,' (___ ) (___) `.__. (___) `.__.'_. `.__,' `.__.'
|
||||
|
||||
-------------------------------------------------------------------------------------------------------
|
||||
=======================================================================================================
|
||||
-------------------------------------------------------------------------------------------------------
|
||||
"""
|
||||
|
||||
boat = """\
|
||||
+ +
|
||||
)`.).
|
||||
)``)``) .~~
|
||||
).-'.-')|)
|
||||
|-).-).-'_'-/
|
||||
~~~\ `o-o-o' /~~~~
|
||||
~~~'---.____/~~~"""
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"presets" : ["es2015", "react"]
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
node_modules/*
|
||||
vendor/*
|
||||
javascripts/dist/*
|
||||
@@ -1,234 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
|
||||
"globals": {
|
||||
"Symbol": false,
|
||||
"Map": false,
|
||||
"Set": false,
|
||||
"Reflect": false,
|
||||
},
|
||||
|
||||
"env": {
|
||||
"es6": false,
|
||||
"browser": true,
|
||||
"node": true,
|
||||
},
|
||||
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 5,
|
||||
"sourceType": "module"
|
||||
},
|
||||
|
||||
"rules": {
|
||||
"array-bracket-spacing": [2, "never", {
|
||||
"singleValue": false,
|
||||
"objectsInArrays": false,
|
||||
"arraysInArrays": false
|
||||
}],
|
||||
"array-callback-return": [2],
|
||||
"block-spacing": [2, "always"],
|
||||
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
|
||||
"callback-return": [2, ["callback"]],
|
||||
"camelcase": [0],
|
||||
"comma-dangle": [2, "never"],
|
||||
"comma-spacing": [2],
|
||||
"comma-style": [2, "last"],
|
||||
"curly": [2, "all"],
|
||||
"eqeqeq": 2,
|
||||
"func-names": [0],
|
||||
"id-length": [2, { "min": 1, "max": 25, "properties": "never" }],
|
||||
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
|
||||
"keyword-spacing": [2, {
|
||||
"before": true,
|
||||
"after": true,
|
||||
"overrides": {
|
||||
"return": { "after": true },
|
||||
"throw": { "after": true },
|
||||
"case": { "after": true }
|
||||
}
|
||||
}],
|
||||
"linebreak-style": [2, "unix"],
|
||||
"lines-around-comment": [2, {
|
||||
"beforeBlockComment": false,
|
||||
"afterBlockComment": false,
|
||||
"beforeLineComment": false,
|
||||
"allowBlockStart": true,
|
||||
"allowBlockEnd": true
|
||||
}],
|
||||
"max-depth": [2, 5],
|
||||
"max-len": [0, 80, 4],
|
||||
"max-nested-callbacks": [1, 3],
|
||||
"max-params": [1, 4],
|
||||
"new-parens": [2],
|
||||
"newline-after-var": [0],
|
||||
"no-bitwise": [0],
|
||||
"no-cond-assign": [2],
|
||||
"no-console": [1, { allow: ["warn", "error"] }],
|
||||
"no-const-assign": [2],
|
||||
"no-constant-condition": [2],
|
||||
"no-control-regex": [2],
|
||||
"no-debugger": [2],
|
||||
"no-delete-var": [2],
|
||||
"no-dupe-args": [2],
|
||||
"no-dupe-class-members": [2],
|
||||
"no-dupe-keys": [2],
|
||||
"no-duplicate-case": [2],
|
||||
"no-else-return": [0],
|
||||
"no-empty": [2],
|
||||
"no-eq-null": [0],
|
||||
"no-eval": [2],
|
||||
"no-ex-assign": [2],
|
||||
"no-extend-native": [2],
|
||||
"no-extra-bind": [2],
|
||||
"no-extra-boolean-cast": [2],
|
||||
"no-extra-label": [2],
|
||||
"no-extra-parens": [0], // needed for clearer #math eg (a - b) / c
|
||||
"no-extra-semi": [2],
|
||||
"no-fallthrough": [2],
|
||||
"no-floating-decimal": [2],
|
||||
"no-func-assign": [2],
|
||||
"no-implied-eval": [2],
|
||||
"no-implicit-coercion": [2, {
|
||||
"boolean": false,
|
||||
"number": true,
|
||||
"string": true
|
||||
}],
|
||||
"no-implicit-globals": [2],
|
||||
"no-inline-comments": [0],
|
||||
"no-invalid-regexp": [2],
|
||||
"no-irregular-whitespace": [2],
|
||||
"no-iterator": [2],
|
||||
"no-label-var": [2],
|
||||
"no-labels": [2, { "allowLoop": false, "allowSwitch": false }],
|
||||
"no-lone-blocks": [2],
|
||||
"no-lonely-if": [2],
|
||||
"no-loop-func": [2],
|
||||
"no-magic-numbers": [0], // doesn't work well with vis cosmetic constant
|
||||
"no-mixed-requires": [1, false],
|
||||
"no-mixed-spaces-and-tabs": [2, false],
|
||||
"no-multi-spaces": [2, {
|
||||
"exceptions": {
|
||||
"ImportDeclaration": true,
|
||||
"Property": true,
|
||||
"VariableDeclarator": true
|
||||
}
|
||||
}],
|
||||
"no-multi-str": [2],
|
||||
"no-multiple-empty-lines": [2, { "max": 1, "maxEOF": 1 }],
|
||||
"no-native-reassign": [2],
|
||||
"no-negated-condition": [2],
|
||||
"no-negated-in-lhs": [2],
|
||||
"no-nested-ternary": [0],
|
||||
"no-new": [2],
|
||||
"no-new-func": [2],
|
||||
"no-new-object": [2],
|
||||
"no-new-require": [0],
|
||||
"no-new-symbol": [2],
|
||||
"no-new-wrappers": [2],
|
||||
"no-obj-calls": [2],
|
||||
"no-octal": [2],
|
||||
"no-octal-escape": [2],
|
||||
"no-path-concat": [0],
|
||||
"no-process-env": [0],
|
||||
"no-process-exit": [2],
|
||||
"no-proto": [2],
|
||||
"no-redeclare": [2],
|
||||
"no-regex-spaces": [2],
|
||||
"no-restricted-modules": [0],
|
||||
"no-restricted-imports": [0],
|
||||
"no-restricted-syntax": [2,
|
||||
"DebuggerStatement",
|
||||
"LabeledStatement",
|
||||
"WithStatement"
|
||||
],
|
||||
"no-return-assign": [2, "always"],
|
||||
"no-script-url": [2],
|
||||
"no-self-assign": [2],
|
||||
"no-self-compare": [0],
|
||||
"no-sequences": [2],
|
||||
"no-shadow-restricted-names": [2],
|
||||
"no-spaced-func": [2],
|
||||
"no-sparse-arrays": [2],
|
||||
"no-sync": [0],
|
||||
"no-ternary": [0],
|
||||
"no-this-before-super": [2],
|
||||
"no-throw-literal": [2],
|
||||
"no-trailing-spaces": [2, { "skipBlankLines": false }],
|
||||
"no-undef": [2, { "typeof": true }],
|
||||
"no-undef-init": [2],
|
||||
"no-undefined": [0],
|
||||
"no-underscore-dangle": [0], // __data__ sometimes
|
||||
"no-unexpected-multiline": [2],
|
||||
"no-unmodified-loop-condition": [2],
|
||||
"no-unneeded-ternary": [2],
|
||||
"no-unreachable": [2],
|
||||
"no-unused-expressions": [2],
|
||||
"no-unused-labels": [2],
|
||||
"no-unused-vars": [2, {
|
||||
"vars": "all",
|
||||
"args": "none", // (d, i) pattern d3 func makes difficult to enforce
|
||||
"varsIgnorePattern": "jQuery"
|
||||
}],
|
||||
"no-use-before-define": [0],
|
||||
"no-useless-call": [2],
|
||||
"no-useless-concat": [2],
|
||||
"no-useless-constructor": [2],
|
||||
"no-void": [0],
|
||||
"no-warning-comments": [0, { "terms": ["todo", "fixme", "xxx"], "location": "start" }],
|
||||
"no-with": [2],
|
||||
"no-whitespace-before-property": [2],
|
||||
"object-curly-spacing": [2, "always"],
|
||||
"object-shorthand": [2, "never"],
|
||||
"one-var": [0],
|
||||
"one-var-declaration-per-line": [2, "initializations"],
|
||||
"operator-assignment": [0, "always"],
|
||||
"padded-blocks": [0],
|
||||
"prefer-arrow-callback": [0],
|
||||
"prefer-const": [0],
|
||||
"prefer-reflect": [0],
|
||||
"prefer-rest-params": [0],
|
||||
"prefer-spread": [0],
|
||||
"prefer-template": [0],
|
||||
"quote-props": [2, "as-needed", { "keywords": true }],
|
||||
"radix": [2],
|
||||
"require-yield": [2],
|
||||
"semi": [2],
|
||||
"semi-spacing": [2, { "before": false, "after": true }],
|
||||
"sort-vars": [0],
|
||||
"sort-imports": [0],
|
||||
"space-before-function-paren": [2, { "anonymous": "always", "named": "never" }],
|
||||
"space-before-blocks": [2, { "functions": "always", "keywords": "always" }],
|
||||
"space-in-brackets": [0, "never", {
|
||||
"singleValue": true,
|
||||
"arraysInArrays": false,
|
||||
"arraysInObjects": false,
|
||||
"objectsInArrays": true,
|
||||
"objectsInObjects": true,
|
||||
"propertyName": false
|
||||
}],
|
||||
},
|
||||
// Temporarily not enforced
|
||||
"new-cap": [2], // @TODO more tricky for the moment
|
||||
"newline-per-chained-call": [2, { "ignoreChainWithDepth": 6 }],
|
||||
"no-param-reassign": [0], // turn on once default args supported
|
||||
"no-shadow": [2, { // @TODO more tricky for the moment with eg 'data'
|
||||
"builtinGlobals": false,
|
||||
"hoist": "functions",
|
||||
"allow": ["i", "d"]
|
||||
}],
|
||||
"space-in-parens": [2, "never"],
|
||||
"space-infix-ops": [2],
|
||||
"space-unary-ops": [2, { "words": true, "nonwords": false }],
|
||||
"spaced-comment": [2, "always", { "markers": ["!"] }],
|
||||
"spaced-line-comment": [0, "always"],
|
||||
"strict": [2, "global"],
|
||||
"template-curly-spacing": [2, "never"],
|
||||
"use-isnan": [2],
|
||||
"valid-jsdoc": [0],
|
||||
"valid-typeof": [2],
|
||||
"vars-on-top": [0],
|
||||
"wrap-iife": [2],
|
||||
"wrap-regex": [2],
|
||||
"yield-star-spacing": [2, { "before": false, "after": true }],
|
||||
"yoda": [2, "never", { "exceptRange": true, "onlyEquality": false }]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 459 KiB |
|
Before Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 236 KiB |
|
Before Width: | Height: | Size: 702 KiB |
|
Before Width: | Height: | Size: 328 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 147 KiB |
|
Before Width: | Height: | Size: 154 KiB |
|
Before Width: | Height: | Size: 190 KiB |
|
Before Width: | Height: | Size: 245 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 133 KiB |
|
Before Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 132 KiB |
|
Before Width: | Height: | Size: 253 KiB |
|
Before Width: | Height: | Size: 242 KiB |
|
Before Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 425 KiB |
|
Before Width: | Height: | Size: 738 KiB |
|
Before Width: | Height: | Size: 314 KiB |
|
Before Width: | Height: | Size: 222 KiB |
|
Before Width: | Height: | Size: 460 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 270 KiB |
|
Before Width: | Height: | Size: 200 KiB |
|
Before Width: | Height: | Size: 170 KiB |
|
Before Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 133 KiB |
@@ -1,5 +0,0 @@
|
||||
require('../node_modules/select2/select2.css');
|
||||
require('../node_modules/select2-bootstrap-css/select2-bootstrap.min.css');
|
||||
require('../node_modules/jquery-ui/themes/base/jquery-ui.css');
|
||||
require('select2');
|
||||
require('../vendor/select2.sortable.js');
|
||||
@@ -1 +0,0 @@
|
||||
require('../stylesheets/less/index.less');
|
||||
@@ -1,298 +0,0 @@
|
||||
var $ = window.$ = require('jquery');
|
||||
var jQuery = window.jQuery = $;
|
||||
var px = require('./modules/caravel.js');
|
||||
var d3 = require('d3');
|
||||
var showModal = require('./modules/utils.js').showModal;
|
||||
require('bootstrap');
|
||||
|
||||
var ace = require('brace');
|
||||
require('brace/mode/css');
|
||||
require('brace/theme/crimson_editor');
|
||||
|
||||
require('./caravel-select2.js');
|
||||
require('../node_modules/gridster/dist/jquery.gridster.min.css');
|
||||
require('../node_modules/gridster/dist/jquery.gridster.min.js');
|
||||
|
||||
require('../stylesheets/dashboard.css');
|
||||
|
||||
var Dashboard = function (dashboardData) {
|
||||
var dashboard = $.extend(dashboardData, {
|
||||
filters: {},
|
||||
init: function () {
|
||||
this.initDashboardView();
|
||||
this.firstLoad = true;
|
||||
px.initFavStars();
|
||||
var sliceObjects = [],
|
||||
dash = this;
|
||||
dashboard.slices.forEach(function (data) {
|
||||
if (data.error) {
|
||||
var html = '<div class="alert alert-danger">' + data.error + '</div>';
|
||||
$("#slice_" + data.slice_id).find('.token').html(html);
|
||||
} else {
|
||||
var slice = px.Slice(data, dash);
|
||||
$("#slice_" + data.slice_id).find('a.refresh').click(function () {
|
||||
slice.render(true);
|
||||
});
|
||||
sliceObjects.push(slice);
|
||||
}
|
||||
});
|
||||
this.slices = sliceObjects;
|
||||
this.refreshTimer = null;
|
||||
this.startPeriodicRender(0);
|
||||
},
|
||||
setFilter: function (slice_id, col, vals) {
|
||||
this.addFilter(slice_id, col, vals, false);
|
||||
},
|
||||
addFilter: function (slice_id, col, vals, merge) {
|
||||
if (merge === undefined) {
|
||||
merge = true;
|
||||
}
|
||||
if (!(slice_id in this.filters)) {
|
||||
this.filters[slice_id] = {};
|
||||
}
|
||||
if (!(col in this.filters[slice_id]) || !merge) {
|
||||
this.filters[slice_id][col] = vals;
|
||||
} else {
|
||||
this.filters[slice_id][col] = d3.merge([this.filters[slice_id][col], vals]);
|
||||
}
|
||||
this.refreshExcept(slice_id);
|
||||
},
|
||||
readFilters: function () {
|
||||
// Returns a list of human readable active filters
|
||||
return JSON.stringify(this.filters, null, 4);
|
||||
},
|
||||
stopPeriodicRender: function () {
|
||||
if (this.refreshTimer) {
|
||||
clearTimeout(this.refreshTimer);
|
||||
this.refreshTimer = null;
|
||||
}
|
||||
},
|
||||
startPeriodicRender: function (interval) {
|
||||
this.stopPeriodicRender();
|
||||
var dash = this;
|
||||
var maxRandomDelay = Math.min(interval * 0.2, 5000);
|
||||
var refreshAll = function () {
|
||||
dash.slices.forEach(function (slice) {
|
||||
var force = !dash.firstLoad;
|
||||
setTimeout(function () {
|
||||
slice.render(force);
|
||||
},
|
||||
//Randomize to prevent all widgets refreshing at the same time
|
||||
maxRandomDelay * Math.random());
|
||||
});
|
||||
dash.firstLoad = false;
|
||||
};
|
||||
|
||||
var fetchAndRender = function () {
|
||||
refreshAll();
|
||||
if (interval > 0) {
|
||||
dash.refreshTimer = setTimeout(function () {
|
||||
fetchAndRender();
|
||||
}, interval);
|
||||
}
|
||||
};
|
||||
fetchAndRender();
|
||||
},
|
||||
refreshExcept: function (slice_id) {
|
||||
var immune = this.metadata.filter_immune_slices || [];
|
||||
this.slices.forEach(function (slice) {
|
||||
if (slice.data.slice_id !== slice_id && immune.indexOf(slice.data.slice_id) === -1) {
|
||||
slice.render();
|
||||
}
|
||||
});
|
||||
},
|
||||
clearFilters: function (slice_id) {
|
||||
delete this.filters[slice_id];
|
||||
this.refreshExcept(slice_id);
|
||||
},
|
||||
removeFilter: function (slice_id, col, vals) {
|
||||
if (slice_id in this.filters) {
|
||||
if (col in this.filters[slice_id]) {
|
||||
var a = [];
|
||||
this.filters[slice_id][col].forEach(function (v) {
|
||||
if (vals.indexOf(v) < 0) {
|
||||
a.push(v);
|
||||
}
|
||||
});
|
||||
this.filters[slice_id][col] = a;
|
||||
}
|
||||
}
|
||||
this.refreshExcept(slice_id);
|
||||
},
|
||||
getSlice: function (slice_id) {
|
||||
slice_id = parseInt(slice_id, 10);
|
||||
for (var i=0; i < this.slices.length; i++) {
|
||||
if (this.slices[i].data.slice_id === slice_id) {
|
||||
return this.slices[i];
|
||||
}
|
||||
}
|
||||
},
|
||||
initDashboardView: function () {
|
||||
dashboard = this;
|
||||
var gridster = $(".gridster ul").gridster({
|
||||
autogrow_cols: true,
|
||||
widget_margins: [10, 10],
|
||||
widget_base_dimensions: [95, 95],
|
||||
draggable: {
|
||||
handle: '.drag'
|
||||
},
|
||||
resize: {
|
||||
enabled: true,
|
||||
stop: function (e, ui, element) {
|
||||
dashboard.getSlice($(element).attr('slice_id')).resize();
|
||||
}
|
||||
},
|
||||
serialize_params: function (_w, wgd) {
|
||||
return {
|
||||
slice_id: $(_w).attr('slice_id'),
|
||||
col: wgd.col,
|
||||
row: wgd.row,
|
||||
size_x: wgd.size_x,
|
||||
size_y: wgd.size_y
|
||||
};
|
||||
}
|
||||
}).data('gridster');
|
||||
|
||||
// Displaying widget controls on hover
|
||||
$('.chart-header').hover(
|
||||
function () {
|
||||
$(this).find('.chart-controls').fadeIn(300);
|
||||
},
|
||||
function () {
|
||||
$(this).find('.chart-controls').fadeOut(300);
|
||||
}
|
||||
);
|
||||
$("div.gridster").css('visibility', 'visible');
|
||||
$("#savedash").click(function () {
|
||||
var expanded_slices = {};
|
||||
$.each($(".slice_info"), function (i, d) {
|
||||
var widget = $(this).parents('.widget');
|
||||
var slice_description = widget.find('.slice_description');
|
||||
if (slice_description.is(":visible")) {
|
||||
expanded_slices[$(d).attr('slice_id')] = true;
|
||||
}
|
||||
});
|
||||
var data = {
|
||||
positions: gridster.serialize(),
|
||||
css: editor.getValue(),
|
||||
expanded_slices: expanded_slices
|
||||
};
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: '/caravel/save_dash/' + dashboard.id + '/',
|
||||
data: {
|
||||
data: JSON.stringify(data)
|
||||
},
|
||||
success: function () {
|
||||
showModal({
|
||||
title: "Success",
|
||||
body: "This dashboard was saved successfully."
|
||||
});
|
||||
},
|
||||
error: function (error) {
|
||||
showModal({
|
||||
title: "Error",
|
||||
body: "Sorry, there was an error saving this dashboard:<br />" + error
|
||||
});
|
||||
console.warn("Save dashboard error", error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var editor = ace.edit("dash_css");
|
||||
editor.$blockScrolling = Infinity;
|
||||
|
||||
editor.setTheme("ace/theme/crimson_editor");
|
||||
editor.setOptions({
|
||||
minLines: 16,
|
||||
maxLines: Infinity,
|
||||
useWorker: false
|
||||
});
|
||||
editor.getSession().setMode("ace/mode/css");
|
||||
|
||||
$(".select2").select2({
|
||||
dropdownAutoWidth: true
|
||||
});
|
||||
$("#css_template").on("change", function () {
|
||||
var css = $(this).find('option:selected').data('css');
|
||||
editor.setValue(css);
|
||||
|
||||
$('#dash_css').val(css);
|
||||
injectCss("dashboard-template", css);
|
||||
|
||||
});
|
||||
$('#filters').click(function () {
|
||||
showModal({
|
||||
title: "<span class='fa fa-info-circle'></span> Current Global Filters",
|
||||
body: "The following global filters are currently applied:<br/>" + dashboard.readFilters()
|
||||
});
|
||||
});
|
||||
$("#refresh_dash_interval").on("change", function () {
|
||||
var interval = $(this).find('option:selected').val() * 1000;
|
||||
dashboard.startPeriodicRender(interval);
|
||||
});
|
||||
$('#refresh_dash').click(function () {
|
||||
dashboard.slices.forEach(function (slice) {
|
||||
slice.render(true);
|
||||
});
|
||||
});
|
||||
$("a.remove-chart").click(function () {
|
||||
var li = $(this).parents("li");
|
||||
gridster.remove_widget(li);
|
||||
});
|
||||
|
||||
$("li.widget").click(function (e) {
|
||||
var $this = $(this);
|
||||
var $target = $(e.target);
|
||||
|
||||
if ($target.hasClass("slice_info")) {
|
||||
$this.find(".slice_description").slideToggle(0, function () {
|
||||
$this.find('.refresh').click();
|
||||
});
|
||||
} else if ($target.hasClass("controls-toggle")) {
|
||||
$this.find(".chart-controls").toggle();
|
||||
}
|
||||
});
|
||||
|
||||
editor.on("change", function () {
|
||||
var css = editor.getValue();
|
||||
$('#dash_css').val(css);
|
||||
injectCss("dashboard-template", css);
|
||||
});
|
||||
|
||||
var css = $('.dashboard').data('css');
|
||||
injectCss("dashboard-template", css);
|
||||
|
||||
// Injects the passed css string into a style sheet with the specified className
|
||||
// If a stylesheet doesn't exist with the passed className, one will be injected into <head>
|
||||
function injectCss(className, css) {
|
||||
|
||||
var head = document.head || document.getElementsByTagName('head')[0];
|
||||
var style = document.querySelector('.' + className);
|
||||
|
||||
if (!style) {
|
||||
if (className.split(' ').length > 1) {
|
||||
throw new Error("This method only supports selections with a single class name.");
|
||||
}
|
||||
style = document.createElement('style');
|
||||
style.className = className;
|
||||
style.type = 'text/css';
|
||||
head.appendChild(style);
|
||||
}
|
||||
|
||||
if (style.styleSheet) {
|
||||
style.styleSheet.cssText = css;
|
||||
} else {
|
||||
style.innerHTML = css;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
dashboard.init();
|
||||
return dashboard;
|
||||
};
|
||||
|
||||
$(document).ready(function () {
|
||||
Dashboard($('.dashboard').data('dashboard'));
|
||||
$('[data-toggle="tooltip"]').tooltip({ container: 'body' });
|
||||
});
|
||||
@@ -1,366 +0,0 @@
|
||||
// Javascript for the explorer page
|
||||
// Init explorer view -> load vis dependencies -> read data (from dynamic html) -> render slice
|
||||
// nb: to add a new vis, you must also add a Python fn in viz.py
|
||||
//
|
||||
// js
|
||||
var $ = window.$ = require('jquery');
|
||||
var jQuery = window.jQuery = $;
|
||||
var px = require('./modules/caravel.js');
|
||||
var showModal = require('./modules/utils.js').showModal;
|
||||
|
||||
require('jquery-ui');
|
||||
$.widget.bridge('uitooltip', $.ui.tooltip); // Shutting down jq-ui tooltips
|
||||
require('bootstrap');
|
||||
|
||||
require('./caravel-select2.js');
|
||||
|
||||
require('../node_modules/bootstrap-toggle/js/bootstrap-toggle.min.js');
|
||||
|
||||
// css
|
||||
require('../vendor/pygments.css');
|
||||
require('../stylesheets/explore.css');
|
||||
require('../node_modules/bootstrap-toggle/css/bootstrap-toggle.min.css');
|
||||
|
||||
var slice;
|
||||
|
||||
function prepForm() {
|
||||
var i = 1;
|
||||
// Assigning the right id to form elements in filters
|
||||
$("#filters > div").each(function () {
|
||||
$(this).attr("id", function () {
|
||||
return "flt_" + i;
|
||||
});
|
||||
$(this).find("#flt_col_0")
|
||||
.attr("id", function () {
|
||||
return "flt_col_" + i;
|
||||
})
|
||||
.attr("name", function () {
|
||||
return "flt_col_" + i;
|
||||
});
|
||||
$(this).find("#flt_op_0")
|
||||
.attr("id", function () {
|
||||
return "flt_op_" + i;
|
||||
})
|
||||
.attr("name", function () {
|
||||
return "flt_op_" + i;
|
||||
});
|
||||
$(this).find("#flt_eq_0")
|
||||
.attr("id", function () {
|
||||
return "flt_eq_" + i;
|
||||
})
|
||||
.attr("name", function () {
|
||||
return "flt_eq_" + i;
|
||||
});
|
||||
i++;
|
||||
});
|
||||
}
|
||||
|
||||
function query(force, pushState) {
|
||||
if (force === undefined) {
|
||||
force = false;
|
||||
}
|
||||
if (pushState !== false) {
|
||||
history.pushState({}, document.title, slice.querystring());
|
||||
}
|
||||
$('.query-and-save button').attr('disabled', 'disabled');
|
||||
$('.btn-group.results span,a').attr('disabled', 'disabled');
|
||||
$('div.alert').remove();
|
||||
$('#is_cached').hide();
|
||||
prepForm();
|
||||
slice.render(force);
|
||||
}
|
||||
|
||||
function initExploreView() {
|
||||
|
||||
function get_collapsed_fieldsets() {
|
||||
var collapsed_fieldsets = $("#collapsed_fieldsets").val();
|
||||
|
||||
if (collapsed_fieldsets !== undefined && collapsed_fieldsets !== "") {
|
||||
collapsed_fieldsets = collapsed_fieldsets.split('||');
|
||||
} else {
|
||||
collapsed_fieldsets = [];
|
||||
}
|
||||
return collapsed_fieldsets;
|
||||
}
|
||||
|
||||
function toggle_fieldset(legend, animation) {
|
||||
var parent = legend.parent();
|
||||
var fieldset = parent.find(".legend_label").text();
|
||||
var collapsed_fieldsets = get_collapsed_fieldsets();
|
||||
var index;
|
||||
|
||||
if (parent.hasClass("collapsed")) {
|
||||
if (animation) {
|
||||
parent.find(".panel-body").slideDown();
|
||||
} else {
|
||||
parent.find(".panel-body").show();
|
||||
}
|
||||
parent.removeClass("collapsed");
|
||||
parent.find("span.collapser").text("[-]");
|
||||
|
||||
// removing from array, js is overcomplicated
|
||||
index = collapsed_fieldsets.indexOf(fieldset);
|
||||
if (index !== -1) {
|
||||
collapsed_fieldsets.splice(index, 1);
|
||||
}
|
||||
} else { // not collapsed
|
||||
if (animation) {
|
||||
parent.find(".panel-body").slideUp();
|
||||
} else {
|
||||
parent.find(".panel-body").hide();
|
||||
}
|
||||
|
||||
parent.addClass("collapsed");
|
||||
parent.find("span.collapser").text("[+]");
|
||||
index = collapsed_fieldsets.indexOf(fieldset);
|
||||
if (index === -1 && fieldset !== "" && fieldset !== undefined) {
|
||||
collapsed_fieldsets.push(fieldset);
|
||||
}
|
||||
}
|
||||
|
||||
$("#collapsed_fieldsets").val(collapsed_fieldsets.join("||"));
|
||||
}
|
||||
|
||||
px.initFavStars();
|
||||
|
||||
$('form .panel-heading').click(function () {
|
||||
toggle_fieldset($(this), true);
|
||||
$(this).css('cursor', 'pointer');
|
||||
});
|
||||
|
||||
function copyURLToClipboard(url) {
|
||||
var textArea = document.createElement("textarea");
|
||||
textArea.style.position = 'fixed';
|
||||
textArea.style.left = '-1000px';
|
||||
textArea.value = url;
|
||||
|
||||
document.body.appendChild(textArea);
|
||||
textArea.select();
|
||||
|
||||
try {
|
||||
var successful = document.execCommand('copy');
|
||||
if (!successful) {
|
||||
throw new Error("Not successful");
|
||||
}
|
||||
} catch (err) {
|
||||
window.alert("Sorry, your browser does not support copying. Use Ctrl / Cmd + C!");
|
||||
}
|
||||
document.body.removeChild(textArea);
|
||||
return successful;
|
||||
}
|
||||
|
||||
$('#shortner').click(function () {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: '/r/shortner/',
|
||||
data: {
|
||||
data: '/' + window.location.pathname + slice.querystring()
|
||||
},
|
||||
success: function (data) {
|
||||
var close = '<a style="cursor: pointer;"><i class="fa fa-close" id="close_shortner"></i></a>';
|
||||
var copy = '<a style="cursor: pointer;"><i class="fa fa-clipboard" title="Copy to clipboard" id="copy_url"></i></a>';
|
||||
var spaces = ' ';
|
||||
var popover = data + spaces + copy + spaces + close;
|
||||
|
||||
var $shortner = $('#shortner')
|
||||
.popover({
|
||||
content: popover,
|
||||
placement: 'left',
|
||||
html: true,
|
||||
trigger: 'manual'
|
||||
})
|
||||
.popover('show');
|
||||
|
||||
$('#copy_url').tooltip().click(function () {
|
||||
var success = copyURLToClipboard(data);
|
||||
if (success) {
|
||||
$(this).attr("data-original-title", "Copied!").tooltip('fixTitle').tooltip('show');
|
||||
window.setTimeout(destroyPopover, 1200);
|
||||
}
|
||||
});
|
||||
$('#close_shortner').click(destroyPopover);
|
||||
|
||||
function destroyPopover() {
|
||||
$shortner.popover('destroy');
|
||||
}
|
||||
},
|
||||
error: function (error) {
|
||||
showModal({
|
||||
title: "Error",
|
||||
body: "Sorry, an error occurred during this operation:<br/>" + error
|
||||
});
|
||||
console.warn("Short URL error", error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#viz_type").change(function () {
|
||||
$("#query").submit();
|
||||
});
|
||||
|
||||
$("#datasource_id").change(function () {
|
||||
var url = $(this).find('option:selected').attr('url');
|
||||
window.location = url;
|
||||
});
|
||||
|
||||
var collapsed_fieldsets = get_collapsed_fieldsets();
|
||||
for (var i = 0; i < collapsed_fieldsets.length; i++) {
|
||||
toggle_fieldset($('legend:contains("' + collapsed_fieldsets[i] + '")'), false);
|
||||
}
|
||||
function formatViz(viz) {
|
||||
var url = '/static/assets/images/viz_thumbnails/' + viz.id + '.png';
|
||||
var no_img = '/static/assets/images/noimg.png';
|
||||
return $(
|
||||
'<img class="viz-thumb-option" src="' + url + '" onerror="this.src=\'' + no_img + '\';">' +
|
||||
'<span>' + viz.text + '</span>'
|
||||
);
|
||||
}
|
||||
|
||||
$(".select2").select2({
|
||||
dropdownAutoWidth: true
|
||||
});
|
||||
$(".select2Sortable").select2({
|
||||
dropdownAutoWidth: true
|
||||
});
|
||||
$(".select2-with-images").select2({
|
||||
dropdownAutoWidth: true,
|
||||
dropdownCssClass: "bigdrop",
|
||||
formatResult: formatViz
|
||||
});
|
||||
$(".select2Sortable").select2Sortable({
|
||||
bindOrder: 'sortableStop'
|
||||
});
|
||||
$("form").show();
|
||||
$('[data-toggle="tooltip"]').tooltip({ container: 'body' });
|
||||
$(".ui-helper-hidden-accessible").remove(); // jQuery-ui 1.11+ creates a div for every tooltip
|
||||
|
||||
function set_filters() {
|
||||
for (var i = 1; i < 10; i++) {
|
||||
var eq = px.getParam("flt_eq_" + i);
|
||||
if (eq !== '') {
|
||||
add_filter(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
set_filters();
|
||||
|
||||
function add_filter(i) {
|
||||
var cp = $("#flt0").clone();
|
||||
$(cp).appendTo("#filters");
|
||||
$(cp).show();
|
||||
if (i !== undefined) {
|
||||
$(cp).find("#flt_eq_0").val(px.getParam("flt_eq_" + i));
|
||||
$(cp).find("#flt_op_0").val(px.getParam("flt_op_" + i));
|
||||
$(cp).find("#flt_col_0").val(px.getParam("flt_col_" + i));
|
||||
}
|
||||
$(cp).find('select').select2();
|
||||
$(cp).find('.remove').click(function () {
|
||||
$(this).parent().parent().remove();
|
||||
});
|
||||
}
|
||||
|
||||
$(window).bind("popstate", function (event) {
|
||||
// Browser back button
|
||||
var returnLocation = history.location || document.location;
|
||||
// Could do something more lightweight here, but we're not optimizing
|
||||
// for the use of the back button anyways
|
||||
returnLocation.reload();
|
||||
});
|
||||
|
||||
$("#plus").click(add_filter);
|
||||
$("#btn_save").click(function () {
|
||||
var slice_name = prompt("Name your slice!");
|
||||
if (slice_name !== "" && slice_name !== null) {
|
||||
$("#slice_name").val(slice_name);
|
||||
prepForm();
|
||||
$("#action").val("save");
|
||||
$("#query").submit();
|
||||
}
|
||||
});
|
||||
$("#btn_overwrite").click(function () {
|
||||
var flag = confirm("Overwrite slice [" + $("#slice_name").val() + "] !?");
|
||||
if (flag) {
|
||||
$("#action").val("overwrite");
|
||||
prepForm();
|
||||
$("#query").submit();
|
||||
}
|
||||
});
|
||||
|
||||
$(".query").click(function () {
|
||||
query(true);
|
||||
});
|
||||
|
||||
function create_choices(term, data) {
|
||||
var filtered = $(data).filter(function () {
|
||||
return this.text.localeCompare(term) === 0;
|
||||
});
|
||||
if (filtered.length === 0) {
|
||||
return {
|
||||
id: term,
|
||||
text: term
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function initSelectionToValue(element, callback) {
|
||||
callback({
|
||||
id: element.val(),
|
||||
text: element.val()
|
||||
});
|
||||
}
|
||||
|
||||
$(".select2_freeform").each(function () {
|
||||
var parent = $(this).parent();
|
||||
var name = $(this).attr('name');
|
||||
var l = [];
|
||||
var selected = '';
|
||||
for (var i = 0; i < this.options.length; i++) {
|
||||
l.push({
|
||||
id: this.options[i].value,
|
||||
text: this.options[i].text
|
||||
});
|
||||
if (this.options[i].selected) {
|
||||
selected = this.options[i].value;
|
||||
}
|
||||
}
|
||||
parent.append(
|
||||
'<input class="' + $(this).attr('class') + '" name="' + name + '" type="text" value="' + selected + '">'
|
||||
);
|
||||
$("input[name='" + name + "']").select2({
|
||||
createSearchChoice: create_choices,
|
||||
initSelection: initSelectionToValue,
|
||||
dropdownAutoWidth: true,
|
||||
multiple: false,
|
||||
data: l
|
||||
});
|
||||
$(this).remove();
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
initExploreView();
|
||||
|
||||
// Dynamically register this visualization
|
||||
var visType = window.viz_type.value;
|
||||
px.registerViz(visType);
|
||||
|
||||
var data = $('.slice').data('slice');
|
||||
slice = px.Slice(data);
|
||||
|
||||
//
|
||||
$('.slice').data('slice', slice);
|
||||
|
||||
// call vis render method, which issues ajax
|
||||
query(false, false);
|
||||
|
||||
// make checkbox inputs display as toggles
|
||||
$(':checkbox')
|
||||
.addClass('pull-right')
|
||||
.attr("data-onstyle", "default")
|
||||
.bootstrapToggle({
|
||||
size: 'mini'
|
||||
});
|
||||
|
||||
$('div.toggle').addClass('pull-right');
|
||||
slice.bindResizeToWindowResize();
|
||||
});
|
||||
@@ -1,18 +0,0 @@
|
||||
var $ = require('jquery');
|
||||
var jQuery = $;
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { Jumbotron } from 'react-bootstrap';
|
||||
|
||||
class App extends React.Component {
|
||||
render () {
|
||||
return (
|
||||
<Jumbotron>
|
||||
<h1>Caravel</h1>
|
||||
<p>Extensible visualization tool for exploring data from any database.</p>
|
||||
</Jumbotron>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render(<App />, document.getElementById('app'));
|
||||
@@ -1,383 +0,0 @@
|
||||
var $ = require('jquery');
|
||||
var jQuery = $;
|
||||
var d3 = require('d3');
|
||||
|
||||
// vis sources
|
||||
var sourceMap = {
|
||||
area: 'nvd3_vis.js',
|
||||
bar: 'nvd3_vis.js',
|
||||
bubble: 'nvd3_vis.js',
|
||||
big_number: 'big_number.js',
|
||||
big_number_total: 'big_number.js',
|
||||
compare: 'nvd3_vis.js',
|
||||
dist_bar: 'nvd3_vis.js',
|
||||
directed_force: 'directed_force.js',
|
||||
filter_box: 'filter_box.js',
|
||||
heatmap: 'heatmap.js',
|
||||
iframe: 'iframe.js',
|
||||
line: 'nvd3_vis.js',
|
||||
markup: 'markup.js',
|
||||
para: 'parallel_coordinates.js',
|
||||
pie: 'nvd3_vis.js',
|
||||
box_plot: 'nvd3_vis.js',
|
||||
pivot_table: 'pivot_table.js',
|
||||
sankey: 'sankey.js',
|
||||
sunburst: 'sunburst.js',
|
||||
table: 'table.js',
|
||||
word_cloud: 'word_cloud.js',
|
||||
world_map: 'world_map.js',
|
||||
treemap: 'treemap.js'
|
||||
};
|
||||
|
||||
var color = function () {
|
||||
// Color related utility functions go in this object
|
||||
var bnbColors = [
|
||||
//rausch hackb kazan babu lima beach barol
|
||||
'#ff5a5f', '#7b0051', '#007A87', '#00d1c1', '#8ce071', '#ffb400', '#b4a76c',
|
||||
'#ff8083', '#cc0086', '#00a1b3', '#00ffeb', '#bbedab', '#ffd266', '#cbc29a',
|
||||
'#ff3339', '#ff1ab1', '#005c66', '#00b3a5', '#55d12e', '#b37e00', '#988b4e'
|
||||
];
|
||||
var spectrums = {
|
||||
blue_white_yellow: ['#00d1c1', 'white', '#ffb400'],
|
||||
fire: ['white', 'yellow', 'red', 'black'],
|
||||
white_black: ['white', 'black'],
|
||||
black_white: ['black', 'white']
|
||||
};
|
||||
var colorBnb = function () {
|
||||
// Color factory
|
||||
var seen = {};
|
||||
return function (s) {
|
||||
if (!s) { return; }
|
||||
// next line is for caravel series that should have the same color
|
||||
s = s.replace('---', '');
|
||||
if (seen[s] === undefined) {
|
||||
seen[s] = Object.keys(seen).length;
|
||||
}
|
||||
return this.bnbColors[seen[s] % this.bnbColors.length];
|
||||
};
|
||||
};
|
||||
var colorScalerFactory = function (colors, data, accessor) {
|
||||
// Returns a linear scaler our of an array of color
|
||||
if (!Array.isArray(colors)) {
|
||||
colors = spectrums[colors];
|
||||
}
|
||||
|
||||
var ext = [0, 1];
|
||||
if (data !== undefined) {
|
||||
ext = d3.extent(data, accessor);
|
||||
}
|
||||
|
||||
var points = [];
|
||||
var chunkSize = (ext[1] - ext[0]) / colors.length;
|
||||
$.each(colors, function (i, c) {
|
||||
points.push(i * chunkSize);
|
||||
});
|
||||
return d3.scale.linear().domain(points).range(colors);
|
||||
};
|
||||
return {
|
||||
bnbColors: bnbColors,
|
||||
category21: colorBnb(),
|
||||
colorScalerFactory: colorScalerFactory
|
||||
};
|
||||
};
|
||||
|
||||
var px = (function () {
|
||||
|
||||
var visualizations = {};
|
||||
var slice;
|
||||
|
||||
function getParam(name) {
|
||||
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
|
||||
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
|
||||
results = regex.exec(location.search);
|
||||
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
|
||||
}
|
||||
|
||||
function UTC(dttm) {
|
||||
return new Date(dttm.getUTCFullYear(), dttm.getUTCMonth(), dttm.getUTCDate(), dttm.getUTCHours(), dttm.getUTCMinutes(), dttm.getUTCSeconds());
|
||||
}
|
||||
var tickMultiFormat = d3.time.format.multi([
|
||||
[".%L", function (d) {
|
||||
return d.getMilliseconds();
|
||||
}], // If there are millisections, show only them
|
||||
[":%S", function (d) {
|
||||
return d.getSeconds();
|
||||
}], // If there are seconds, show only them
|
||||
["%a %b %d, %I:%M %p", function (d) {
|
||||
return d.getMinutes() !== 0;
|
||||
}], // If there are non-zero minutes, show Date, Hour:Minute [AM/PM]
|
||||
["%a %b %d, %I %p", function (d) {
|
||||
return d.getHours() !== 0;
|
||||
}], // If there are hours that are multiples of 3, show date and AM/PM
|
||||
["%a %b %d, %Y", function (d) {
|
||||
return d.getDate() !== 1;
|
||||
}], // If not the first of the month, do "month day, year."
|
||||
["%B %Y", function (d) {
|
||||
return d.getMonth() !== 0 && d.getDate() === 1;
|
||||
}], // If the first of the month, do "month day, year."
|
||||
["%Y", function (d) {
|
||||
return true;
|
||||
}] // fall back on month, year
|
||||
]);
|
||||
|
||||
function formatDate(dttm) {
|
||||
var d = UTC(new Date(dttm));
|
||||
//d = new Date(d.getTime() - 1 * 60 * 60 * 1000);
|
||||
return tickMultiFormat(d);
|
||||
}
|
||||
|
||||
function timeFormatFactory(d3timeFormat) {
|
||||
var f = d3.time.format(d3timeFormat);
|
||||
return function (dttm) {
|
||||
var d = UTC(new Date(dttm));
|
||||
return f(d);
|
||||
};
|
||||
}
|
||||
|
||||
function initFavStars() {
|
||||
var baseUrl = '/caravel/favstar/';
|
||||
// Init star behavihor for favorite
|
||||
function show() {
|
||||
if ($(this).hasClass('selected')) {
|
||||
$(this).html('<i class="fa fa-star"></i>');
|
||||
} else {
|
||||
$(this).html('<i class="fa fa-star-o"></i>');
|
||||
}
|
||||
}
|
||||
$('.favstar')
|
||||
.attr('title', 'Click to favorite/unfavorite')
|
||||
.each(show)
|
||||
.each(function () {
|
||||
var url = baseUrl + $(this).attr("class_name");
|
||||
var star = this;
|
||||
url += '/' + $(this).attr("obj_id") + '/';
|
||||
$.getJSON(url + 'count/', function (data) {
|
||||
if (data.count > 0) {
|
||||
$(star)
|
||||
.addClass('selected')
|
||||
.each(show);
|
||||
}
|
||||
});
|
||||
})
|
||||
.click(function () {
|
||||
$(this).toggleClass('selected');
|
||||
var url = baseUrl + $(this).attr("class_name");
|
||||
url += '/' + $(this).attr("obj_id") + '/';
|
||||
if ($(this).hasClass('selected')) {
|
||||
url += 'select/';
|
||||
} else {
|
||||
url += 'unselect/';
|
||||
}
|
||||
$.get(url);
|
||||
$(this).each(show);
|
||||
})
|
||||
.tooltip();
|
||||
}
|
||||
|
||||
var Slice = function (data, dashboard) {
|
||||
var timer;
|
||||
var token = $('#' + data.token);
|
||||
var container_id = data.token + '_con';
|
||||
var selector = '#' + container_id;
|
||||
var container = $(selector);
|
||||
var slice_id = data.slice_id;
|
||||
var dttm = 0;
|
||||
var stopwatch = function () {
|
||||
dttm += 10;
|
||||
var num = dttm / 1000;
|
||||
$('#timer').text(num.toFixed(2) + " sec");
|
||||
};
|
||||
var qrystr = '';
|
||||
var always = function (data) {
|
||||
//Private f, runs after done and error
|
||||
clearInterval(timer);
|
||||
$('#timer').removeClass('btn-warning');
|
||||
};
|
||||
slice = {
|
||||
data: data,
|
||||
container: container,
|
||||
container_id: container_id,
|
||||
selector: selector,
|
||||
querystring: function () {
|
||||
var parser = document.createElement('a');
|
||||
parser.href = data.json_endpoint;
|
||||
if (dashboard !== undefined) {
|
||||
var flts = encodeURIComponent(JSON.stringify(dashboard.filters));
|
||||
qrystr = parser.search + "&extra_filters=" + flts;
|
||||
} else if ($('#query').length === 0) {
|
||||
qrystr = parser.search;
|
||||
} else {
|
||||
qrystr = '?' + $('#query').serialize();
|
||||
}
|
||||
return qrystr;
|
||||
},
|
||||
getWidgetHeader: function () {
|
||||
return this.container.parents("li.widget").find(".chart-header");
|
||||
},
|
||||
jsonEndpoint: function () {
|
||||
var parser = document.createElement('a');
|
||||
parser.href = data.json_endpoint;
|
||||
var endpoint = parser.pathname + this.querystring();
|
||||
endpoint += "&json=true";
|
||||
endpoint += "&force=" + this.force;
|
||||
return endpoint;
|
||||
},
|
||||
done: function (data) {
|
||||
clearInterval(timer);
|
||||
token.find("img.loading").hide();
|
||||
container.show();
|
||||
|
||||
var cachedSelector = null;
|
||||
if (dashboard === undefined) {
|
||||
cachedSelector = $('#is_cached');
|
||||
if (data !== undefined && data.is_cached) {
|
||||
cachedSelector
|
||||
.attr('title', 'Served from data cached at ' + data.cached_dttm + '. Click to force-refresh')
|
||||
.show()
|
||||
.tooltip('fixTitle');
|
||||
} else {
|
||||
cachedSelector.hide();
|
||||
}
|
||||
} else {
|
||||
var refresh = this.getWidgetHeader().find('.refresh');
|
||||
if (data !== undefined && data.is_cached) {
|
||||
refresh
|
||||
.addClass('danger')
|
||||
.attr(
|
||||
'title',
|
||||
'Served from data cached at ' + data.cached_dttm + '. Click to force-refresh')
|
||||
.tooltip('fixTitle');
|
||||
} else {
|
||||
refresh
|
||||
.removeClass('danger')
|
||||
.attr(
|
||||
'title',
|
||||
'Click to force-refresh')
|
||||
.tooltip('fixTitle');
|
||||
}
|
||||
}
|
||||
if (data !== undefined) {
|
||||
$("#query_container").html(data.query);
|
||||
}
|
||||
$('#timer').removeClass('btn-warning');
|
||||
$('#timer').addClass('btn-success');
|
||||
$('span.query').removeClass('disabled');
|
||||
$('#json').click(function () {
|
||||
window.location = data.json_endpoint;
|
||||
});
|
||||
$('#standalone').click(function () {
|
||||
window.location = data.standalone_endpoint;
|
||||
});
|
||||
$('#csv').click(function () {
|
||||
window.location = data.csv_endpoint;
|
||||
});
|
||||
$('.btn-group.results span,a').removeAttr('disabled');
|
||||
$('.query-and-save button').removeAttr('disabled');
|
||||
always(data);
|
||||
},
|
||||
error: function (msg) {
|
||||
token.find("img.loading").hide();
|
||||
var err = '<div class="alert alert-danger">' + msg + '</div>';
|
||||
container.html(err);
|
||||
container.show();
|
||||
$('span.query').removeClass('disabled');
|
||||
$('#timer').addClass('btn-danger');
|
||||
$('.btn-group.results span,a').removeAttr('disabled');
|
||||
$('.query-and-save button').removeAttr('disabled');
|
||||
always(data);
|
||||
},
|
||||
width: function () {
|
||||
return token.width();
|
||||
},
|
||||
height: function () {
|
||||
var others = 0;
|
||||
var widget = container.parents('.widget');
|
||||
var slice_description = widget.find('.slice_description');
|
||||
if (slice_description.is(":visible")) {
|
||||
others += widget.find('.slice_description').height() + 25;
|
||||
}
|
||||
others += widget.find('.chart-header').height();
|
||||
return widget.height() - others - 10;
|
||||
},
|
||||
bindResizeToWindowResize: function () {
|
||||
var resizeTimer;
|
||||
var slice = this;
|
||||
$(window).on('resize', function (e) {
|
||||
clearTimeout(resizeTimer);
|
||||
resizeTimer = setTimeout(function () {
|
||||
slice.resize();
|
||||
}, 500);
|
||||
});
|
||||
},
|
||||
render: function (force) {
|
||||
if (force === undefined) {
|
||||
force = false;
|
||||
}
|
||||
this.force = force;
|
||||
token.find("img.loading").show();
|
||||
container.css('height', this.height());
|
||||
dttm = 0;
|
||||
timer = setInterval(stopwatch, 10);
|
||||
$('#timer').removeClass('btn-danger btn-success');
|
||||
$('#timer').addClass('btn-warning');
|
||||
this.viz.render();
|
||||
},
|
||||
resize: function () {
|
||||
token.find("img.loading").show();
|
||||
container.css('height', this.height());
|
||||
this.viz.render();
|
||||
this.viz.resize();
|
||||
},
|
||||
addFilter: function (col, vals) {
|
||||
if (dashboard !== undefined) {
|
||||
dashboard.addFilter(slice_id, col, vals);
|
||||
}
|
||||
},
|
||||
setFilter: function (col, vals) {
|
||||
if (dashboard !== undefined) {
|
||||
dashboard.setFilter(slice_id, col, vals);
|
||||
}
|
||||
},
|
||||
clearFilter: function () {
|
||||
if (dashboard !== undefined) {
|
||||
delete dashboard.clearFilter(slice_id);
|
||||
}
|
||||
},
|
||||
removeFilter: function (col, vals) {
|
||||
if (dashboard !== undefined) {
|
||||
delete dashboard.removeFilter(slice_id, col, vals);
|
||||
}
|
||||
}
|
||||
};
|
||||
var visType = data.form_data.viz_type;
|
||||
px.registerViz(visType);
|
||||
slice.viz = visualizations[data.form_data.viz_type](slice);
|
||||
return slice;
|
||||
};
|
||||
|
||||
function registerViz(name) {
|
||||
var visSource = sourceMap[name];
|
||||
|
||||
if (visSource) {
|
||||
var visFactory = require('../../visualizations/' + visSource);
|
||||
if (typeof visFactory === 'function') {
|
||||
visualizations[name] = visFactory;
|
||||
}
|
||||
} else {
|
||||
throw new Error("require(" + name + ") failed.");
|
||||
}
|
||||
}
|
||||
|
||||
// Export public functions
|
||||
return {
|
||||
registerViz: registerViz,
|
||||
Slice: Slice,
|
||||
formatDate: formatDate,
|
||||
timeFormatFactory: timeFormatFactory,
|
||||
color: color(),
|
||||
getParam: getParam,
|
||||
initFavStars: initFavStars
|
||||
};
|
||||
})();
|
||||
|
||||
module.exports = px;
|
||||
@@ -1,80 +0,0 @@
|
||||
var $ = require('jquery');
|
||||
var d3 = require('d3');
|
||||
|
||||
/*
|
||||
Utility function that takes a d3 svg:text selection and a max width, and splits the
|
||||
text's text across multiple tspan lines such that any given line does not exceed max width
|
||||
|
||||
If text does not span multiple lines AND adjustedY is passed, will set the text to the passed val
|
||||
*/
|
||||
function wrapSvgText(text, width, adjustedY) {
|
||||
var lineHeight = 1; // ems
|
||||
|
||||
text.each(function () {
|
||||
var text = d3.select(this),
|
||||
words = text.text().split(/\s+/),
|
||||
word,
|
||||
line = [],
|
||||
lineNumber = 0,
|
||||
x = text.attr("x"),
|
||||
y = text.attr("y"),
|
||||
dy = parseFloat(text.attr("dy")),
|
||||
tspan = text.text(null)
|
||||
.append("tspan")
|
||||
.attr("x", x)
|
||||
.attr("y", y)
|
||||
.attr("dy", dy + "em");
|
||||
|
||||
var didWrap = false;
|
||||
|
||||
for (var i = 0; i < words.length; i++) {
|
||||
word = words[i];
|
||||
line.push(word);
|
||||
tspan.text(line.join(" "));
|
||||
|
||||
if (tspan.node().getComputedTextLength() > width) {
|
||||
line.pop(); // remove word that pushes over the limit
|
||||
tspan.text(line.join(" "));
|
||||
line = [word];
|
||||
tspan = text.append("tspan")
|
||||
.attr("x", x)
|
||||
.attr("y", y)
|
||||
.attr("dy", ++lineNumber * lineHeight + dy + "em")
|
||||
.text(word);
|
||||
|
||||
didWrap = true;
|
||||
}
|
||||
}
|
||||
if (!didWrap && typeof adjustedY !== "undefined") {
|
||||
tspan.attr("y", adjustedY);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the body and title content of a modal, and shows it. Assumes HTML for modal exists and that
|
||||
* it handles closing (i.e., works with bootstrap)
|
||||
*
|
||||
* @param {object} options object of the form
|
||||
* {
|
||||
* title: {string},
|
||||
* body: {string},
|
||||
* modalSelector: {string, default: '.misc-modal' },
|
||||
* titleSelector: {string, default: '.misc-modal .modal-title' },
|
||||
* bodySelector: {string, default: '.misc-modal .modal-body' },
|
||||
* }
|
||||
*/
|
||||
function showModal(options) {
|
||||
options.modalSelector = options.modalSelector || ".misc-modal";
|
||||
options.titleSelector = options.titleSelector || ".misc-modal .modal-title";
|
||||
options.bodySelector = options.bodySelector || ".misc-modal .modal-body";
|
||||
|
||||
$(options.titleSelector).html(options.title || "");
|
||||
$(options.bodySelector).html(options.body || "");
|
||||
$(options.modalSelector).modal("show");
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
wrapSvgText: wrapSvgText,
|
||||
showModal: showModal
|
||||
};
|
||||
@@ -1,106 +0,0 @@
|
||||
var $ = window.$ = require('jquery');
|
||||
var jQuery = window.jQuery = $;
|
||||
var showModal = require('./modules/utils.js').showModal;
|
||||
|
||||
require('./caravel-select2.js');
|
||||
|
||||
require('datatables.net-bs');
|
||||
require('../node_modules/datatables-bootstrap3-plugin/media/css/datatables-bootstrap3.css');
|
||||
require('bootstrap');
|
||||
|
||||
var ace = require('brace');
|
||||
require('brace/mode/sql');
|
||||
require('brace/theme/crimson_editor');
|
||||
|
||||
require('../stylesheets/sql.css');
|
||||
|
||||
$(document).ready(function () {
|
||||
function getParam(name) {
|
||||
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
|
||||
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
|
||||
results = regex.exec(location.search);
|
||||
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
|
||||
}
|
||||
|
||||
function initSqlEditorView() {
|
||||
var database_id = $('#database_id').val();
|
||||
var editor = ace.edit("sql");
|
||||
editor.$blockScrolling = Infinity;
|
||||
editor.getSession().setUseWrapMode(true);
|
||||
|
||||
$('#sql').hide();
|
||||
editor.setTheme("ace/theme/crimson_editor");
|
||||
editor.setOptions({
|
||||
minLines: 16,
|
||||
maxLines: Infinity
|
||||
});
|
||||
editor.getSession().setMode("ace/mode/sql");
|
||||
editor.focus();
|
||||
$("select").select2({
|
||||
dropdownAutoWidth: true
|
||||
});
|
||||
|
||||
function showTableMetadata() {
|
||||
$(".metadata").load(
|
||||
'/caravel/table/' + database_id + '/' + $("#dbtable").val() + '/');
|
||||
}
|
||||
$("#dbtable").on("change", showTableMetadata);
|
||||
showTableMetadata();
|
||||
$("#create_view").click(function () {
|
||||
showModal({
|
||||
title: "Error",
|
||||
body: "Sorry, this feature is not yet implemented"
|
||||
});
|
||||
});
|
||||
$(".sqlcontent").show();
|
||||
|
||||
function selectStarOnClick() {
|
||||
$.ajax('/caravel/select_star/' + database_id + '/' + $("#dbtable").val() + '/')
|
||||
.done(function (msg) {
|
||||
editor.setValue(msg);
|
||||
});
|
||||
}
|
||||
|
||||
$("#select_star").click(selectStarOnClick);
|
||||
|
||||
editor.setValue(getParam('sql'));
|
||||
$(window).bind("popstate", function (event) {
|
||||
// Could do something more lightweight here, but we're not optimizing
|
||||
// for the use of the back button anyways
|
||||
editor.setValue(getParam('sql'));
|
||||
$("#run").click();
|
||||
});
|
||||
$("#run").click(function () {
|
||||
$('#results').hide(0);
|
||||
$('#loading').show(0);
|
||||
history.pushState({}, document.title, '?sql=' + encodeURIComponent(editor.getValue()));
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: '/caravel/runsql/',
|
||||
data: {
|
||||
data: JSON.stringify({
|
||||
database_id: $('#database_id').val(),
|
||||
sql: editor.getSession().getValue()
|
||||
})
|
||||
},
|
||||
success: function (data) {
|
||||
$('#loading').hide(0);
|
||||
$('#results').show(0);
|
||||
$('#results').html(data);
|
||||
|
||||
$('table.sql_results').DataTable({
|
||||
paging: false,
|
||||
searching: true,
|
||||
aaSorting: []
|
||||
});
|
||||
},
|
||||
error: function (err, err2) {
|
||||
$('#loading').hide(0);
|
||||
$('#results').show(0);
|
||||
$('#results').html(err.responseText);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
initSqlEditorView();
|
||||
});
|
||||
@@ -1,13 +0,0 @@
|
||||
var $ = window.$ = require('jquery');
|
||||
var jQuery = window.jQuery = $;
|
||||
var px = require('./modules/caravel.js');
|
||||
|
||||
require('bootstrap');
|
||||
|
||||
$(document).ready(function () {
|
||||
var slice;
|
||||
var data = $('.slice').data('slice');
|
||||
slice = px.Slice(data);
|
||||
slice.render();
|
||||
slice.bindResizeToWindowResize();
|
||||
});
|
||||
@@ -1,70 +0,0 @@
|
||||
var $ = window.$ = require('jquery');
|
||||
var jQuery = window.jQuery = $;
|
||||
|
||||
require('../stylesheets/welcome.css');
|
||||
require('bootstrap');
|
||||
require('datatables.net-bs');
|
||||
require('../node_modules/datatables-bootstrap3-plugin/media/css/datatables-bootstrap3.css');
|
||||
require('../node_modules/cal-heatmap/cal-heatmap.css');
|
||||
|
||||
var CalHeatMap = require('cal-heatmap');
|
||||
|
||||
function modelViewTable(selector, modelView, orderCol, order) {
|
||||
// Builds a dataTable from a flask appbuilder api endpoint
|
||||
var url = '/' + modelView.toLowerCase() + '/api/read';
|
||||
url += '?_oc_' + modelView + '=' + orderCol;
|
||||
url += '&_od_' + modelView +'=' + order;
|
||||
$.getJSON(url, function (data) {
|
||||
var tableData = jQuery.map(data.result, function (el, i) {
|
||||
var row = $.map(data.list_columns, function (col, i) {
|
||||
return el[col];
|
||||
});
|
||||
return [row];
|
||||
});
|
||||
var cols = jQuery.map(data.list_columns, function (col, i) {
|
||||
return { sTitle: data.label_columns[col] };
|
||||
});
|
||||
var panel = $(selector).parents('.panel');
|
||||
panel.find("img.loading").remove();
|
||||
$(selector).DataTable({
|
||||
aaData: tableData,
|
||||
aoColumns: cols,
|
||||
bPaginate: true,
|
||||
pageLength: 10,
|
||||
bLengthChange: false,
|
||||
aaSorting: [],
|
||||
searching: true,
|
||||
bInfo: false
|
||||
});
|
||||
|
||||
// Hack to move the searchbox in the right spot
|
||||
var search = panel.find(".dataTables_filter input");
|
||||
search.addClass('form-control').detach();
|
||||
search.appendTo(panel.find(".search"));
|
||||
panel.find('.dataTables_filter').remove();
|
||||
|
||||
// Hack to display the page navigator properly
|
||||
panel.find('.col-sm-5').remove();
|
||||
var nav = panel.find('.col-sm-7');
|
||||
nav.removeClass('col-sm-7');
|
||||
nav.addClass('col-sm-12');
|
||||
|
||||
$(selector).slideDown();
|
||||
$('[data-toggle="tooltip"]').tooltip({ container: 'body' });
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
var cal = new CalHeatMap();
|
||||
cal.init({
|
||||
start: new Date().setFullYear(new Date().getFullYear() - 1),
|
||||
range: 13,
|
||||
data: '/caravel/activity_per_day',
|
||||
domain: "month",
|
||||
subDomain: "day",
|
||||
itemName: "action",
|
||||
tooltip: true
|
||||
});
|
||||
modelViewTable('#dash_table', 'DashboardModelViewAsync', 'changed_on', 'desc');
|
||||
modelViewTable('#slice_table', 'SliceAsync', 'changed_on', 'desc');
|
||||
});
|
||||
@@ -1,78 +0,0 @@
|
||||
{
|
||||
"name": "caravel",
|
||||
"version": "0.1.0",
|
||||
"description": "Any database to any visualization",
|
||||
"directories": {
|
||||
"doc": "docs",
|
||||
"test": "tests"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"dev": "webpack -d --watch --colors",
|
||||
"prod": "webpack -p --colors",
|
||||
"lint": "npm run --silent lint:js",
|
||||
"lint:js": "eslint --ignore-path=.eslintignore --ext .js ."
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/airbnb/caravel.git"
|
||||
},
|
||||
"keywords": [
|
||||
"big",
|
||||
"data",
|
||||
"exploratory",
|
||||
"analysis",
|
||||
"react",
|
||||
"d3",
|
||||
"airbnb",
|
||||
"nerds",
|
||||
"database",
|
||||
"flask"
|
||||
],
|
||||
"author": "Airbnb",
|
||||
"bugs": {
|
||||
"url": "https://github.com/airbnb/caravel/issues"
|
||||
},
|
||||
"homepage": "https://github.com/airbnb/caravel#readme",
|
||||
"dependencies": {
|
||||
"babel-loader": "^6.2.1",
|
||||
"babel-polyfill": "^6.3.14",
|
||||
"babel-preset-es2015": "^6.3.13",
|
||||
"babel-preset-react": "^6.3.13",
|
||||
"bootstrap": "^3.3.6",
|
||||
"bootstrap-datepicker": "^1.6.0",
|
||||
"bootstrap-toggle": "^2.2.1",
|
||||
"brace": "^0.7.0",
|
||||
"cal-heatmap": "3.5.4",
|
||||
"css-loader": "^0.23.1",
|
||||
"d3": "^3.5.14",
|
||||
"d3-cloud": "^1.2.1",
|
||||
"d3-sankey": "^0.2.1",
|
||||
"d3-tip": "^0.6.7",
|
||||
"datamaps": "^0.4.4",
|
||||
"datatables-bootstrap3-plugin": "^0.4.0",
|
||||
"datatables.net-bs": "^1.10.11",
|
||||
"exports-loader": "^0.6.3",
|
||||
"font-awesome": "^4.5.0",
|
||||
"gridster": "^0.5.6",
|
||||
"imports-loader": "^0.6.5",
|
||||
"jquery": "^2.2.1",
|
||||
"jquery-ui": "^1.10.5",
|
||||
"less": "^2.6.1",
|
||||
"less-loader": "^2.2.2",
|
||||
"nvd3": "1.8.2",
|
||||
"react": "^0.14.7",
|
||||
"react-bootstrap": "^0.28.3",
|
||||
"react-dom": "^0.14.7",
|
||||
"select2": "3.5",
|
||||
"select2-bootstrap-css": "^1.4.6",
|
||||
"style-loader": "^0.13.0",
|
||||
"topojson": "^1.6.22",
|
||||
"webpack": "^1.12.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^2.2.0",
|
||||
"file-loader": "^0.8.5",
|
||||
"url-loader": "^0.5.7"
|
||||
}
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
body {
|
||||
margin: 0px !important;
|
||||
}
|
||||
|
||||
.emph {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.alert.alert-danger > .debugger {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.modal-dialog {
|
||||
z-index: 1100;
|
||||
}
|
||||
.label {
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
.no-wrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
input.form-control {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.chart-header a.danger {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.disabledButton {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.col-left-fixed {
|
||||
width:350px;
|
||||
position: absolute;
|
||||
float: left;
|
||||
}
|
||||
.col-offset {
|
||||
margin-left: 365px;
|
||||
}
|
||||
|
||||
.favstar {
|
||||
margin-right: 10px;
|
||||
opacity: 0.5;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.slice_description{
|
||||
padding: 8px;
|
||||
margin: 5px;
|
||||
border: 1px solid #DDD;
|
||||
background-color: #F8F8F8;
|
||||
border-radius: 5px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.slice_info{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.padded {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.intable-longtext{
|
||||
max-height: 200px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.container-fluid {
|
||||
text-align: left;
|
||||
}
|
||||
input[type="checkbox"] {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
float: right;
|
||||
}
|
||||
form div {
|
||||
padding-top: 1px;
|
||||
}
|
||||
.navbar-brand a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
.navbar-brand a:hover {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.header span {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.widget-is-cached {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.header span.label {
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
#timer {
|
||||
width: 80px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.notbtn {
|
||||
cursor: default;
|
||||
box-shadow: none;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
hr {
|
||||
margin-top: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
span.title-block {
|
||||
background-color: #EEE;
|
||||
border-radius: 4px;
|
||||
padding: 6px 12px;
|
||||
margin: 0px 10px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.nvtooltip {
|
||||
//position: relative !important;
|
||||
z-index: 888;
|
||||
}
|
||||
.nvtooltip table td{
|
||||
font-size: 11px !important;
|
||||
}
|
||||
.navbar {
|
||||
-webkit-box-shadow: 0px 3px 3px #AAA;
|
||||
-moz-box-shadow: 0px 3px 3px #AAA;
|
||||
box-shadow: 0px 3px 3px #AAA;
|
||||
z-index: 999;
|
||||
}
|
||||
.panel.panel-primary {
|
||||
margin: 10px;
|
||||
}
|
||||
.datasource form div.form-control {
|
||||
margin-bottom: 5px !important;
|
||||
}
|
||||
.datasource form input.form-control {
|
||||
margin-bottom: 5px !important;
|
||||
}
|
||||
.datasource .tooltip-inner {
|
||||
max-width: 350px;
|
||||
}
|
||||
img.loading {
|
||||
width: 40px;
|
||||
}
|
||||
img.viz-thumb-option {
|
||||
width: 100px;
|
||||
border: 1px solid gray;
|
||||
margin-right: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.select2-drop.bigdrop .select2-results {
|
||||
max-height: 700px;
|
||||
}
|
||||
|
||||
|
||||
div.header {
|
||||
font-weight: bold;
|
||||
}
|
||||
li.widget:hover {
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
li.widget .chart-header {
|
||||
padding: 5px;
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
li.widget .chart-header a {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
#is_cached {
|
||||
display: none;
|
||||
}
|
||||
|
||||
li.widget .chart-controls {
|
||||
background-color: #f1f1f1;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
padding: 0px 5px;
|
||||
opacity: 0.75;
|
||||
display: none;
|
||||
}
|
||||
|
||||
li.widget .slice_container {
|
||||
overflow: auto;
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
.dashboard a i {
|
||||
cursor: pointer;
|
||||
}
|
||||
.dashboard i.drag {
|
||||
cursor: move !important;
|
||||
}
|
||||
.dashboard .gridster .preview-holder {
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
background-color: #AAA;
|
||||
border-color: #AAA;
|
||||
opacity: 0.3;
|
||||
}
|
||||
.gridster li.widget{
|
||||
list-style-type: none;
|
||||
border-radius: 0;
|
||||
margin: 5px;
|
||||
border: 1px solid #ccc;
|
||||
box-shadow: 2px 1px 5px -2px #aaa;
|
||||
background-color: #fff;
|
||||
}
|
||||
.dashboard .gridster .dragging,
|
||||
.dashboard .gridster .resizing {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.dashboard img.loading {
|
||||
width: 20px;
|
||||
margin: 5px;
|
||||
position: absolute;
|
||||
}
|
||||
.dashboard .title {
|
||||
text-align: center;
|
||||
}
|
||||
.dashboard .slice_title {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
padding: 5px;
|
||||
}
|
||||
.dashboard div.slice_content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.dashboard div.nvtooltip {
|
||||
z-index: 888; /* this lets tool tips go on top of other slices */
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
.widget {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.slice_container {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
@@ -1,616 +0,0 @@
|
||||
// Paper 3.3.5
|
||||
// Bootswatch
|
||||
// -----------------------------------------------------
|
||||
|
||||
@web-font-path: "https://fonts.googleapis.com/css?family=Roboto:300,400,500,700";
|
||||
|
||||
.web-font(@path) {
|
||||
@import url("@{path}");
|
||||
}
|
||||
.web-font(@web-font-path);
|
||||
|
||||
// Navbar =====================================================================
|
||||
|
||||
.navbar {
|
||||
border: none;
|
||||
.box-shadow(0 1px 2px rgba(0,0,0,.3));
|
||||
|
||||
&-brand {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
&-inverse {
|
||||
.navbar-form {
|
||||
|
||||
input[type=text],
|
||||
input[type=password] {
|
||||
color: #fff;
|
||||
.box-shadow(inset 0 -1px 0 @navbar-inverse-link-color);
|
||||
.placeholder(@navbar-inverse-link-color);
|
||||
|
||||
&:focus {
|
||||
.box-shadow(inset 0 -2px 0 #fff);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Buttons ====================================================================
|
||||
|
||||
#btn(@class,@bg) {
|
||||
.btn-@{class} {
|
||||
background-size: 200%;
|
||||
background-position: 50%;
|
||||
|
||||
&:focus {
|
||||
background-color: @bg;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:active:hover {
|
||||
background-color: darken(@bg, 6%);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: darken(@bg, 12%);
|
||||
#gradient > .radial(darken(@bg, 12%) 10%, @bg 11%);
|
||||
background-size: 1000%;
|
||||
.box-shadow(2px 2px 4px rgba(0,0,0,.4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#btn(default,@btn-default-bg);
|
||||
#btn(primary,@btn-primary-bg);
|
||||
#btn(success,@btn-success-bg);
|
||||
#btn(info,@btn-info-bg);
|
||||
#btn(warning,@btn-warning-bg);
|
||||
#btn(danger,@btn-danger-bg);
|
||||
#btn(link,#fff);
|
||||
|
||||
.btn {
|
||||
text-transform: uppercase;
|
||||
border: none;
|
||||
.box-shadow(1px 1px 4px rgba(0,0,0,.4));
|
||||
.transition(all 0.4s);
|
||||
|
||||
&-link {
|
||||
border-radius: @btn-border-radius-base;
|
||||
.box-shadow(none);
|
||||
color: @btn-default-color;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
.box-shadow(none);
|
||||
color: @btn-default-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-default {
|
||||
|
||||
&.disabled {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
.btn + .btn,
|
||||
.btn + .btn-group,
|
||||
.btn-group + .btn,
|
||||
.btn-group + .btn-group {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&-vertical {
|
||||
> .btn + .btn,
|
||||
> .btn + .btn-group,
|
||||
> .btn-group + .btn,
|
||||
> .btn-group + .btn-group {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Typography =================================================================
|
||||
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
letter-spacing: .1px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 1em;
|
||||
}
|
||||
|
||||
input,
|
||||
button {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
letter-spacing: .1px;
|
||||
}
|
||||
|
||||
a {
|
||||
.transition(all 0.2s);
|
||||
}
|
||||
|
||||
// Tables =====================================================================
|
||||
|
||||
.table-hover {
|
||||
> tbody > tr,
|
||||
> tbody > tr > th,
|
||||
> tbody > tr > td {
|
||||
.transition(all 0.2s);
|
||||
}
|
||||
}
|
||||
|
||||
// Forms ======================================================================
|
||||
|
||||
label {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
textarea,
|
||||
textarea.form-control,
|
||||
input.form-control,
|
||||
input[type=text],
|
||||
input[type=password],
|
||||
input[type=email],
|
||||
input[type=number],
|
||||
[type=text].form-control,
|
||||
[type=password].form-control,
|
||||
[type=email].form-control,
|
||||
[type=tel].form-control,
|
||||
[contenteditable].form-control {
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
-webkit-appearance: none;
|
||||
.box-shadow(inset 0 -1px 0 #ddd);
|
||||
font-size: 16px;
|
||||
|
||||
&:focus {
|
||||
.box-shadow(inset 0 -2px 0 @brand-primary);
|
||||
}
|
||||
|
||||
&[disabled],
|
||||
&[readonly] {
|
||||
.box-shadow(none);
|
||||
border-bottom: 1px dotted #ddd;
|
||||
}
|
||||
|
||||
&.input {
|
||||
&-sm {
|
||||
font-size: @font-size-small;
|
||||
}
|
||||
|
||||
&-lg {
|
||||
font-size: @font-size-large;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
select,
|
||||
select.form-control {
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
padding-left: 0;
|
||||
padding-right: 0\9; // remove padding for < ie9 since default arrow can't be removed
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAAAJ1BMVEVmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmaP/QSjAAAADHRSTlMAAgMJC0uWpKa6wMxMdjkoAAAANUlEQVR4AeXJyQEAERAAsNl7Hf3X6xt0QL6JpZWq30pdvdadme+0PMdzvHm8YThHcT1H7K0BtOMDniZhWOgAAAAASUVORK5CYII=);
|
||||
background-size: 13px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: right center;
|
||||
.box-shadow(inset 0 -1px 0 #ddd);
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
|
||||
&::-ms-expand {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.input {
|
||||
&-sm {
|
||||
font-size: @font-size-small;
|
||||
}
|
||||
|
||||
&-lg {
|
||||
font-size: @font-size-large;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
.box-shadow(inset 0 -2px 0 @brand-primary);
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAAAJ1BMVEUhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISF8S9ewAAAADHRSTlMAAgMJC0uWpKa6wMxMdjkoAAAANUlEQVR4AeXJyQEAERAAsNl7Hf3X6xt0QL6JpZWq30pdvdadme+0PMdzvHm8YThHcT1H7K0BtOMDniZhWOgAAAAASUVORK5CYII=);
|
||||
}
|
||||
|
||||
&[multiple] {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
||||
.radio,
|
||||
.radio-inline,
|
||||
.checkbox,
|
||||
.checkbox-inline {
|
||||
label {
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
margin-left: -25px;
|
||||
}
|
||||
}
|
||||
|
||||
input[type="radio"],
|
||||
.radio input[type="radio"],
|
||||
.radio-inline input[type="radio"] {
|
||||
position: relative;
|
||||
margin-top: 6px;
|
||||
margin-right: 4px;
|
||||
vertical-align: top;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
.transition(240ms);
|
||||
}
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: -3px;
|
||||
background-color: @brand-primary;
|
||||
.scale(0);
|
||||
}
|
||||
|
||||
&:after {
|
||||
position: relative;
|
||||
top: -3px;
|
||||
border: 2px solid @gray;
|
||||
}
|
||||
|
||||
&:checked:before {
|
||||
.scale(0.5);
|
||||
}
|
||||
|
||||
&:disabled:checked:before {
|
||||
background-color: @gray-light;
|
||||
}
|
||||
|
||||
&:checked:after {
|
||||
border-color: @brand-primary;
|
||||
}
|
||||
|
||||
&:disabled:after,
|
||||
&:disabled:checked:after {
|
||||
border-color: @gray-light;
|
||||
}
|
||||
}
|
||||
|
||||
input[type="checkbox"],
|
||||
.checkbox input[type="checkbox"],
|
||||
.checkbox-inline input[type="checkbox"] {
|
||||
position: relative;
|
||||
border: none;
|
||||
margin-bottom: -4px;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:focus:after {
|
||||
border-color: @brand-primary;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-top: -2px;
|
||||
margin-right: 5px;
|
||||
border: 2px solid @gray;
|
||||
border-radius: 2px;
|
||||
.transition(240ms);
|
||||
}
|
||||
|
||||
&:checked:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 6px;
|
||||
display: table;
|
||||
width: 6px;
|
||||
height: 12px;
|
||||
border: 2px solid #fff;
|
||||
border-top-width: 0;
|
||||
border-left-width: 0;
|
||||
.rotate(45deg);
|
||||
}
|
||||
|
||||
&:checked:after {
|
||||
background-color: @brand-primary;
|
||||
border-color: @brand-primary;
|
||||
}
|
||||
|
||||
&:disabled:after {
|
||||
border-color: @gray-light;
|
||||
}
|
||||
|
||||
&:disabled:checked:after {
|
||||
background-color: @gray-light;
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.has-warning {
|
||||
input:not([type=checkbox]),
|
||||
.form-control,
|
||||
input.form-control[readonly],
|
||||
input[type=text][readonly],
|
||||
[type=text].form-control[readonly],
|
||||
input:not([type=checkbox]):focus,
|
||||
.form-control:focus {
|
||||
border-bottom: none;
|
||||
.box-shadow(inset 0 -2px 0 @brand-warning);
|
||||
}
|
||||
}
|
||||
|
||||
.has-error {
|
||||
input:not([type=checkbox]),
|
||||
.form-control,
|
||||
input.form-control[readonly],
|
||||
input[type=text][readonly],
|
||||
[type=text].form-control[readonly],
|
||||
input:not([type=checkbox]):focus,
|
||||
.form-control:focus {
|
||||
border-bottom: none;
|
||||
.box-shadow(inset 0 -2px 0 @brand-danger);
|
||||
}
|
||||
}
|
||||
|
||||
.has-success {
|
||||
input:not([type=checkbox]),
|
||||
.form-control,
|
||||
input.form-control[readonly],
|
||||
input[type=text][readonly],
|
||||
[type=text].form-control[readonly],
|
||||
input:not([type=checkbox]):focus,
|
||||
.form-control:focus {
|
||||
border-bottom: none;
|
||||
.box-shadow(inset 0 -2px 0 @brand-success);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the Bootstrap feedback styles for input addons
|
||||
.input-group-addon {
|
||||
.has-warning &, .has-error &, .has-success & {
|
||||
color: @input-color;
|
||||
border-color: @input-group-addon-border-color;
|
||||
background-color: @input-group-addon-bg;
|
||||
}
|
||||
}
|
||||
|
||||
// Navs =======================================================================
|
||||
|
||||
.nav-tabs {
|
||||
> li > a,
|
||||
> li > a:focus {
|
||||
margin-right: 0;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: @navbar-default-link-color;
|
||||
.box-shadow(inset 0 -1px 0 #ddd);
|
||||
.transition(all 0.2s);
|
||||
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
.box-shadow(inset 0 -2px 0 @brand-primary);
|
||||
color: @brand-primary;
|
||||
}
|
||||
}
|
||||
|
||||
& > li.active > a,
|
||||
& > li.active > a:focus {
|
||||
border: none;
|
||||
.box-shadow(inset 0 -2px 0 @brand-primary);
|
||||
color: @brand-primary;
|
||||
|
||||
&:hover {
|
||||
border: none;
|
||||
color: @brand-primary;
|
||||
}
|
||||
}
|
||||
|
||||
& > li.disabled > a {
|
||||
.box-shadow(inset 0 -1px 0 #ddd);
|
||||
}
|
||||
|
||||
&.nav-justified {
|
||||
|
||||
& > li > a,
|
||||
& > li > a:hover,
|
||||
& > li > a:focus,
|
||||
& > .active > a,
|
||||
& > .active > a:hover,
|
||||
& > .active > a:focus {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
margin-top: 0;
|
||||
border: none;
|
||||
.box-shadow(0 1px 4px rgba(0,0,0,.3));
|
||||
}
|
||||
|
||||
// Indicators =================================================================
|
||||
|
||||
.alert {
|
||||
border: none;
|
||||
color: #fff;
|
||||
|
||||
&-success {
|
||||
background-color: @brand-success;
|
||||
}
|
||||
|
||||
&-info {
|
||||
background-color: @brand-info;
|
||||
}
|
||||
|
||||
&-warning {
|
||||
background-color: @brand-warning;
|
||||
}
|
||||
|
||||
&-danger {
|
||||
background-color: @brand-danger;
|
||||
}
|
||||
|
||||
a:not(.close),
|
||||
.alert-link {
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.close {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.badge {
|
||||
padding: 4px 6px 4px;
|
||||
}
|
||||
|
||||
.progress {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
height: 6px;
|
||||
border-radius: 0;
|
||||
|
||||
.box-shadow(none);
|
||||
|
||||
&-bar {
|
||||
.box-shadow(none);
|
||||
|
||||
&:last-child {
|
||||
border-radius: 0 3px 3px 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
&:before {
|
||||
display: block;
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: -1;
|
||||
background-color: lighten(@progress-bar-bg, 35%);
|
||||
}
|
||||
}
|
||||
|
||||
&-success:last-child.progress-bar:before {
|
||||
background-color: lighten(@brand-success, 35%);
|
||||
}
|
||||
|
||||
&-info:last-child.progress-bar:before {
|
||||
background-color: lighten(@brand-info, 45%);
|
||||
}
|
||||
&-warning:last-child.progress-bar:before {
|
||||
background-color: lighten(@brand-warning, 35%);
|
||||
}
|
||||
|
||||
&-danger:last-child.progress-bar:before {
|
||||
background-color: lighten(@brand-danger, 25%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Progress bars ==============================================================
|
||||
|
||||
// Containers =================================================================
|
||||
|
||||
.close {
|
||||
font-size: 34px;
|
||||
font-weight: 300;
|
||||
line-height: 24px;
|
||||
opacity: 0.6;
|
||||
.transition(all 0.2s);
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.list-group {
|
||||
|
||||
&-item {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
&-item-text {
|
||||
color: @gray-light;
|
||||
}
|
||||
}
|
||||
|
||||
.well {
|
||||
border-radius: 0;
|
||||
.box-shadow(none);
|
||||
}
|
||||
|
||||
.panel {
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
.box-shadow(0 1px 4px rgba(0,0,0,.3));
|
||||
|
||||
&-heading {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&-footer {
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
|
||||
.popover {
|
||||
border: none;
|
||||
.box-shadow(0 1px 4px rgba(0,0,0,.3));
|
||||
}
|
||||
|
||||
.carousel {
|
||||
&-caption {
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
// Index .less, any imports here will be included in the final css build
|
||||
|
||||
@import "~bootstrap/less/bootstrap.less";
|
||||
@import "./variables.less";
|
||||
@import "./bootswatch.less";
|
||||
@@ -1,881 +0,0 @@
|
||||
// Modified from Bootswatch Paper 3.3.6
|
||||
// Variables
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
//== Colors
|
||||
//
|
||||
//## Airbnb colors
|
||||
@rausch: #ff5a5f; // coral
|
||||
@kazan: #007a87; // dark teal
|
||||
@hackberry: #7b0051; // purple
|
||||
@babu: #00d1c1; // light teal
|
||||
@lima: #8ce071; // bright green
|
||||
@beach: #ffb400; // yellow
|
||||
@ebisu: #ffaa91; // peach
|
||||
@tirol: #b4a76c; // khaki
|
||||
@foggy: #9CA299; // dark grey
|
||||
@hof: #565A5C; // light grey
|
||||
|
||||
//## Gray and brand colors for use across Bootstrap.
|
||||
|
||||
@gray-base: #000;
|
||||
@gray-darker: lighten(@gray-base, 13.5%); // #222
|
||||
@gray-dark: #212121;
|
||||
@gray: #666;
|
||||
@gray-light: #bbb;
|
||||
@gray-lighter: lighten(@gray-base, 93.5%); // #eee
|
||||
|
||||
@brand-primary: darken(@babu, 5%);
|
||||
@brand-success: darken(@lima, 15%);
|
||||
@brand-info: @beach;
|
||||
@brand-warning: @hackberry;
|
||||
@brand-danger: darken(@rausch, 5%);
|
||||
|
||||
|
||||
//== Scaffolding
|
||||
//
|
||||
//## Settings for some of the most global styles.
|
||||
|
||||
//** Background color for `<body>`.
|
||||
@body-bg: #fff;
|
||||
//** Global text color on `<body>`.
|
||||
@text-color: @gray;
|
||||
|
||||
//** Global textual link color.
|
||||
@link-color: @brand-primary;
|
||||
//** Link hover color set via `darken()` function.
|
||||
@link-hover-color: darken(@link-color, 15%);
|
||||
//** Link hover decoration.
|
||||
@link-hover-decoration: underline;
|
||||
|
||||
|
||||
//== Typography
|
||||
//
|
||||
//## Font, line-height, and color for body text, headings, and more.
|
||||
|
||||
@font-family-sans-serif: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
@font-family-serif: Georgia, "Times New Roman", Times, serif;
|
||||
//** Default monospace fonts for `<code>`, `<kbd>`, and `<pre>`.
|
||||
@font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
@font-family-base: @font-family-sans-serif;
|
||||
|
||||
@font-size-base: 13px;
|
||||
@font-size-large: ceil((@font-size-base * 1.25)); // ~18px
|
||||
@font-size-small: ceil((@font-size-base * 0.85)); // ~12px
|
||||
|
||||
@font-size-h1: 56px;
|
||||
@font-size-h2: 45px;
|
||||
@font-size-h3: 34px;
|
||||
@font-size-h4: 24px;
|
||||
@font-size-h5: 20px;
|
||||
@font-size-h6: 14px;
|
||||
|
||||
//** Unit-less `line-height` for use in components like buttons.
|
||||
@line-height-base: 1.846; // 20/14
|
||||
//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
|
||||
@line-height-computed: floor((@font-size-base * @line-height-base)); // ~20px
|
||||
|
||||
//** By default, this inherits from the `<body>`.
|
||||
@headings-font-family: inherit;
|
||||
@headings-font-weight: 400;
|
||||
@headings-line-height: 1.1;
|
||||
@headings-color: #444;
|
||||
|
||||
|
||||
//== Iconography
|
||||
//
|
||||
//## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower.
|
||||
|
||||
//** Load fonts from this directory.
|
||||
@icon-font-path: "../fonts/";
|
||||
//** File name for all font files.
|
||||
@icon-font-name: "glyphicons-halflings-regular";
|
||||
//** Element ID within SVG icon file.
|
||||
@icon-font-svg-id: "glyphicons_halflingsregular";
|
||||
|
||||
|
||||
//== Components
|
||||
//
|
||||
//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
|
||||
|
||||
@padding-base-vertical: 6px;
|
||||
@padding-base-horizontal: 16px;
|
||||
|
||||
@padding-large-vertical: 10px;
|
||||
@padding-large-horizontal: 16px;
|
||||
|
||||
@padding-small-vertical: 5px;
|
||||
@padding-small-horizontal: 10px;
|
||||
|
||||
@padding-xs-vertical: 1px;
|
||||
@padding-xs-horizontal: 5px;
|
||||
|
||||
@line-height-large: 1.3333333; // extra decimals for Win 8.1 Chrome
|
||||
@line-height-small: 1.5;
|
||||
|
||||
@border-radius-base: 3px;
|
||||
@border-radius-large: 3px;
|
||||
@border-radius-small: 3px;
|
||||
|
||||
//** Global color for active items (e.g., navs or dropdowns).
|
||||
@component-active-color: #fff;
|
||||
//** Global background color for active items (e.g., navs or dropdowns).
|
||||
@component-active-bg: @brand-primary;
|
||||
|
||||
//** Width of the `border` for generating carets that indicator dropdowns.
|
||||
@caret-width-base: 4px;
|
||||
//** Carets increase slightly in size for larger components.
|
||||
@caret-width-large: 5px;
|
||||
|
||||
|
||||
//== Tables
|
||||
//
|
||||
//## Customizes the `.table` component with basic values, each used across all table variations.
|
||||
|
||||
//** Padding for `<th>`s and `<td>`s.
|
||||
@table-cell-padding: 8px;
|
||||
//** Padding for cells in `.table-condensed`.
|
||||
@table-condensed-cell-padding: 5px;
|
||||
|
||||
//** Default background color used for all tables.
|
||||
@table-bg: transparent;
|
||||
//** Background color used for `.table-striped`.
|
||||
@table-bg-accent: #f9f9f9;
|
||||
//** Background color used for `.table-hover`.
|
||||
@table-bg-hover: @gray-lighter;
|
||||
@table-bg-active: @table-bg-hover;
|
||||
|
||||
//** Border color for table and cell borders.
|
||||
@table-border-color: #ddd;
|
||||
|
||||
|
||||
//== Buttons
|
||||
//
|
||||
//## For each of Bootstrap's buttons, define text, background and border color.
|
||||
|
||||
@btn-font-weight: normal;
|
||||
|
||||
@btn-default-color: #444;
|
||||
@btn-default-bg: #fff;
|
||||
@btn-default-border: transparent;
|
||||
|
||||
@btn-primary-color: #fff;
|
||||
@btn-primary-bg: @brand-primary;
|
||||
@btn-primary-border: transparent;
|
||||
|
||||
@btn-success-color: #fff;
|
||||
@btn-success-bg: @brand-success;
|
||||
@btn-success-border: transparent;
|
||||
|
||||
@btn-info-color: #fff;
|
||||
@btn-info-bg: @brand-info;
|
||||
@btn-info-border: transparent;
|
||||
|
||||
@btn-warning-color: #fff;
|
||||
@btn-warning-bg: @brand-warning;
|
||||
@btn-warning-border: transparent;
|
||||
|
||||
@btn-danger-color: #fff;
|
||||
@btn-danger-bg: @brand-danger;
|
||||
@btn-danger-border: transparent;
|
||||
|
||||
@btn-link-disabled-color: @gray-light;
|
||||
|
||||
// Allows for customizing button radius independently from global border radius
|
||||
@btn-border-radius-base: @border-radius-base;
|
||||
@btn-border-radius-large: @border-radius-large;
|
||||
@btn-border-radius-small: @border-radius-small;
|
||||
|
||||
|
||||
//== Forms
|
||||
//
|
||||
//##
|
||||
|
||||
//** `<input>` background color
|
||||
@input-bg: transparent;
|
||||
//** `<input disabled>` background color
|
||||
@input-bg-disabled: transparent;
|
||||
|
||||
//** Text color for `<input>`s
|
||||
@input-color: @gray;
|
||||
//** `<input>` border color
|
||||
@input-border: transparent;
|
||||
|
||||
// TODO: Rename `@input-border-radius` to `@input-border-radius-base` in v4
|
||||
//** Default `.form-control` border radius
|
||||
// This has no effect on `<select>`s in some browsers, due to the limited stylability of `<select>`s in CSS.
|
||||
@input-border-radius: @border-radius-base;
|
||||
//** Large `.form-control` border radius
|
||||
@input-border-radius-large: @border-radius-large;
|
||||
//** Small `.form-control` border radius
|
||||
@input-border-radius-small: @border-radius-small;
|
||||
|
||||
//** Border color for inputs on focus
|
||||
@input-border-focus: #66afe9;
|
||||
|
||||
//** Placeholder text color
|
||||
@input-color-placeholder: @gray-light;
|
||||
|
||||
//** Default `.form-control` height
|
||||
@input-height-base: (@line-height-computed + (@padding-base-vertical * 2) + 2);
|
||||
//** Large `.form-control` height
|
||||
@input-height-large: (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2);
|
||||
//** Small `.form-control` height
|
||||
@input-height-small: (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2);
|
||||
|
||||
//** `.form-group` margin
|
||||
@form-group-margin-bottom: 15px;
|
||||
|
||||
@legend-color: @gray-dark;
|
||||
@legend-border-color: #e5e5e5;
|
||||
|
||||
//** Background color for textual input addons
|
||||
@input-group-addon-bg: transparent;
|
||||
//** Border color for textual input addons
|
||||
@input-group-addon-border-color: @input-border;
|
||||
|
||||
//** Disabled cursor for form controls and buttons.
|
||||
@cursor-disabled: not-allowed;
|
||||
|
||||
|
||||
//== Dropdowns
|
||||
//
|
||||
//## Dropdown menu container and contents.
|
||||
|
||||
//** Background for the dropdown menu.
|
||||
@dropdown-bg: #fff;
|
||||
//** Dropdown menu `border-color`.
|
||||
@dropdown-border: rgba(0,0,0,.15);
|
||||
//** Dropdown menu `border-color` **for IE8**.
|
||||
@dropdown-fallback-border: #ccc;
|
||||
//** Divider color for between dropdown items.
|
||||
@dropdown-divider-bg: #e5e5e5;
|
||||
|
||||
//** Dropdown link text color.
|
||||
@dropdown-link-color: @text-color;
|
||||
//** Hover color for dropdown links.
|
||||
@dropdown-link-hover-color: darken(@gray-dark, 5%);
|
||||
//** Hover background for dropdown links.
|
||||
@dropdown-link-hover-bg: @gray-lighter;
|
||||
|
||||
//** Active dropdown menu item text color.
|
||||
@dropdown-link-active-color: @component-active-color;
|
||||
//** Active dropdown menu item background color.
|
||||
@dropdown-link-active-bg: @component-active-bg;
|
||||
|
||||
//** Disabled dropdown menu item background color.
|
||||
@dropdown-link-disabled-color: @gray-light;
|
||||
|
||||
//** Text color for headers within dropdown menus.
|
||||
@dropdown-header-color: @gray-light;
|
||||
|
||||
//** Deprecated `@dropdown-caret-color` as of v3.1.0
|
||||
@dropdown-caret-color: @gray-light;
|
||||
|
||||
|
||||
//-- Z-index master list
|
||||
//
|
||||
// Warning: Avoid customizing these values. They're used for a bird's eye view
|
||||
// of components dependent on the z-axis and are designed to all work together.
|
||||
//
|
||||
// Note: These variables are not generated into the Customizer.
|
||||
|
||||
@zindex-navbar: 1000;
|
||||
@zindex-dropdown: 1000;
|
||||
@zindex-popover: 1060;
|
||||
@zindex-tooltip: 1070;
|
||||
@zindex-navbar-fixed: 1030;
|
||||
@zindex-modal-background: 1040;
|
||||
@zindex-modal: 1050;
|
||||
|
||||
|
||||
//== Media queries breakpoints
|
||||
//
|
||||
//## Define the breakpoints at which your layout will change, adapting to different screen sizes.
|
||||
|
||||
// Extra small screen / phone
|
||||
//** Deprecated `@screen-xs` as of v3.0.1
|
||||
@screen-xs: 480px;
|
||||
//** Deprecated `@screen-xs-min` as of v3.2.0
|
||||
@screen-xs-min: @screen-xs;
|
||||
//** Deprecated `@screen-phone` as of v3.0.1
|
||||
@screen-phone: @screen-xs-min;
|
||||
|
||||
// Small screen / tablet
|
||||
//** Deprecated `@screen-sm` as of v3.0.1
|
||||
@screen-sm: 768px;
|
||||
@screen-sm-min: @screen-sm;
|
||||
//** Deprecated `@screen-tablet` as of v3.0.1
|
||||
@screen-tablet: @screen-sm-min;
|
||||
|
||||
// Medium screen / desktop
|
||||
//** Deprecated `@screen-md` as of v3.0.1
|
||||
@screen-md: 992px;
|
||||
@screen-md-min: @screen-md;
|
||||
//** Deprecated `@screen-desktop` as of v3.0.1
|
||||
@screen-desktop: @screen-md-min;
|
||||
|
||||
// Large screen / wide desktop
|
||||
//** Deprecated `@screen-lg` as of v3.0.1
|
||||
@screen-lg: 1200px;
|
||||
@screen-lg-min: @screen-lg;
|
||||
//** Deprecated `@screen-lg-desktop` as of v3.0.1
|
||||
@screen-lg-desktop: @screen-lg-min;
|
||||
|
||||
// So media queries don't overlap when required, provide a maximum
|
||||
@screen-xs-max: (@screen-sm-min - 1);
|
||||
@screen-sm-max: (@screen-md-min - 1);
|
||||
@screen-md-max: (@screen-lg-min - 1);
|
||||
|
||||
|
||||
//== Grid system
|
||||
//
|
||||
//## Define your custom responsive grid.
|
||||
|
||||
//** Number of columns in the grid.
|
||||
@grid-columns: 12;
|
||||
//** Padding between columns. Gets divided in half for the left and right.
|
||||
@grid-gutter-width: 30px;
|
||||
// Navbar collapse
|
||||
//** Point at which the navbar becomes uncollapsed.
|
||||
@grid-float-breakpoint: @screen-sm-min;
|
||||
//** Point at which the navbar begins collapsing.
|
||||
@grid-float-breakpoint-max: (@grid-float-breakpoint - 1);
|
||||
|
||||
|
||||
//== Container sizes
|
||||
//
|
||||
//## Define the maximum width of `.container` for different screen sizes.
|
||||
|
||||
// Small screen / tablet
|
||||
@container-tablet: (720px + @grid-gutter-width);
|
||||
//** For `@screen-sm-min` and up.
|
||||
@container-sm: @container-tablet;
|
||||
|
||||
// Medium screen / desktop
|
||||
@container-desktop: (940px + @grid-gutter-width);
|
||||
//** For `@screen-md-min` and up.
|
||||
@container-md: @container-desktop;
|
||||
|
||||
// Large screen / wide desktop
|
||||
@container-large-desktop: (1140px + @grid-gutter-width);
|
||||
//** For `@screen-lg-min` and up.
|
||||
@container-lg: @container-large-desktop;
|
||||
|
||||
|
||||
//== Navbar
|
||||
//
|
||||
//##
|
||||
|
||||
// Basics of a navbar
|
||||
@navbar-height: 64px;
|
||||
@navbar-margin-bottom: @line-height-computed;
|
||||
@navbar-border-radius: @border-radius-base;
|
||||
@navbar-padding-horizontal: floor((@grid-gutter-width / 2));
|
||||
@navbar-padding-vertical: ((@navbar-height - @line-height-computed) / 2);
|
||||
@navbar-collapse-max-height: 340px;
|
||||
|
||||
@navbar-default-color: @gray-light;
|
||||
@navbar-default-bg: #fff;
|
||||
@navbar-default-border: transparent;
|
||||
|
||||
// Navbar links
|
||||
@navbar-default-link-color: @gray;
|
||||
@navbar-default-link-hover-color: @gray-dark;
|
||||
@navbar-default-link-hover-bg: transparent;
|
||||
@navbar-default-link-active-color: @gray-dark;
|
||||
@navbar-default-link-active-bg: darken(@navbar-default-bg, 6.5%);
|
||||
@navbar-default-link-disabled-color: #ccc;
|
||||
@navbar-default-link-disabled-bg: transparent;
|
||||
|
||||
// Navbar brand label
|
||||
@navbar-default-brand-color: @navbar-default-link-color;
|
||||
@navbar-default-brand-hover-color: @navbar-default-link-hover-color;
|
||||
@navbar-default-brand-hover-bg: transparent;
|
||||
|
||||
// Navbar toggle
|
||||
@navbar-default-toggle-hover-bg: transparent;
|
||||
@navbar-default-toggle-icon-bar-bg: rgba(0,0,0,0.5);
|
||||
@navbar-default-toggle-border-color: transparent;
|
||||
|
||||
|
||||
//=== Inverted navbar
|
||||
// Reset inverted navbar basics
|
||||
@navbar-inverse-color: @gray-light;
|
||||
@navbar-inverse-bg: @brand-primary;
|
||||
@navbar-inverse-border: transparent;
|
||||
|
||||
// Inverted navbar links
|
||||
@navbar-inverse-link-color: lighten(@brand-primary, 30%);
|
||||
@navbar-inverse-link-hover-color: #fff;
|
||||
@navbar-inverse-link-hover-bg: transparent;
|
||||
@navbar-inverse-link-active-color: @navbar-inverse-link-hover-color;
|
||||
@navbar-inverse-link-active-bg: darken(@navbar-inverse-bg, 10%);
|
||||
@navbar-inverse-link-disabled-color: #444;
|
||||
@navbar-inverse-link-disabled-bg: transparent;
|
||||
|
||||
// Inverted navbar brand label
|
||||
@navbar-inverse-brand-color: @navbar-inverse-link-color;
|
||||
@navbar-inverse-brand-hover-color: #fff;
|
||||
@navbar-inverse-brand-hover-bg: transparent;
|
||||
|
||||
// Inverted navbar toggle\
|
||||
@navbar-inverse-toggle-hover-bg: transparent;
|
||||
@navbar-inverse-toggle-icon-bar-bg: rgba(0,0,0,0.5);
|
||||
@navbar-inverse-toggle-border-color: transparent;
|
||||
|
||||
|
||||
//== Navs
|
||||
//
|
||||
//##
|
||||
|
||||
//=== Shared nav styles
|
||||
@nav-link-padding: 10px 15px;
|
||||
@nav-link-hover-bg: @gray-lighter;
|
||||
|
||||
@nav-disabled-link-color: @gray-light;
|
||||
@nav-disabled-link-hover-color: @gray-light;
|
||||
|
||||
//== Tabs
|
||||
@nav-tabs-border-color: transparent;
|
||||
|
||||
@nav-tabs-link-hover-border-color: @gray-lighter;
|
||||
|
||||
@nav-tabs-active-link-hover-bg: transparent;
|
||||
@nav-tabs-active-link-hover-color: @gray;
|
||||
@nav-tabs-active-link-hover-border-color: transparent;
|
||||
|
||||
@nav-tabs-justified-link-border-color: @nav-tabs-border-color;
|
||||
@nav-tabs-justified-active-link-border-color: @body-bg;
|
||||
|
||||
//== Pills
|
||||
@nav-pills-border-radius: @border-radius-base;
|
||||
@nav-pills-active-link-hover-bg: @component-active-bg;
|
||||
@nav-pills-active-link-hover-color: @component-active-color;
|
||||
|
||||
|
||||
//== Pagination
|
||||
//
|
||||
//##
|
||||
|
||||
@pagination-color: @link-color;
|
||||
@pagination-bg: #fff;
|
||||
@pagination-border: #ddd;
|
||||
|
||||
@pagination-hover-color: @link-hover-color;
|
||||
@pagination-hover-bg: @gray-lighter;
|
||||
@pagination-hover-border: #ddd;
|
||||
|
||||
@pagination-active-color: #fff;
|
||||
@pagination-active-bg: @brand-primary;
|
||||
@pagination-active-border: @brand-primary;
|
||||
|
||||
@pagination-disabled-color: @gray-light;
|
||||
@pagination-disabled-bg: #fff;
|
||||
@pagination-disabled-border: #ddd;
|
||||
|
||||
|
||||
//== Pager
|
||||
//
|
||||
//##
|
||||
|
||||
@pager-bg: @pagination-bg;
|
||||
@pager-border: @pagination-border;
|
||||
@pager-border-radius: 15px;
|
||||
|
||||
@pager-hover-bg: @pagination-hover-bg;
|
||||
|
||||
@pager-active-bg: @pagination-active-bg;
|
||||
@pager-active-color: @pagination-active-color;
|
||||
|
||||
@pager-disabled-color: @pagination-disabled-color;
|
||||
|
||||
|
||||
//== Jumbotron
|
||||
//
|
||||
//##
|
||||
|
||||
@jumbotron-padding: 30px;
|
||||
@jumbotron-color: inherit;
|
||||
@jumbotron-bg: #f9f9f9;
|
||||
@jumbotron-heading-color: @headings-color;
|
||||
@jumbotron-font-size: ceil((@font-size-base * 1.5));
|
||||
@jumbotron-heading-font-size: ceil((@font-size-base * 4.5));
|
||||
|
||||
|
||||
//== Form states and alerts
|
||||
//
|
||||
//## Define colors for form feedback states and, by default, alerts.
|
||||
|
||||
@state-success-text: @brand-success;
|
||||
@state-success-bg: #dff0d8;
|
||||
@state-success-border: darken(spin(@state-success-bg, -10), 5%);
|
||||
|
||||
@state-info-text: @brand-info;
|
||||
@state-info-bg: #e1bee7;
|
||||
@state-info-border: darken(spin(@state-info-bg, -10), 7%);
|
||||
|
||||
@state-warning-text: @brand-warning;
|
||||
@state-warning-bg: #ffe0b2;
|
||||
@state-warning-border: darken(spin(@state-warning-bg, -10), 5%);
|
||||
|
||||
@state-danger-text: @brand-danger;
|
||||
@state-danger-bg: #f9bdbb;
|
||||
@state-danger-border: darken(spin(@state-danger-bg, -10), 5%);
|
||||
|
||||
|
||||
//== Tooltips
|
||||
//
|
||||
//##
|
||||
|
||||
//** Tooltip max width
|
||||
@tooltip-max-width: 200px;
|
||||
//** Tooltip text color
|
||||
@tooltip-color: #fff;
|
||||
//** Tooltip background color
|
||||
@tooltip-bg: #727272;
|
||||
@tooltip-opacity: .9;
|
||||
|
||||
//** Tooltip arrow width
|
||||
@tooltip-arrow-width: 5px;
|
||||
//** Tooltip arrow color
|
||||
@tooltip-arrow-color: @tooltip-bg;
|
||||
|
||||
|
||||
//== Popovers
|
||||
//
|
||||
//##
|
||||
|
||||
//** Popover body background color
|
||||
@popover-bg: #fff;
|
||||
//** Popover maximum width
|
||||
@popover-max-width: 276px;
|
||||
//** Popover border color
|
||||
@popover-border-color: transparent;
|
||||
//** Popover fallback border color
|
||||
@popover-fallback-border-color: transparent;
|
||||
|
||||
//** Popover title background color
|
||||
@popover-title-bg: darken(@popover-bg, 3%);
|
||||
|
||||
//** Popover arrow width
|
||||
@popover-arrow-width: 10px;
|
||||
//** Popover arrow color
|
||||
@popover-arrow-color: @popover-bg;
|
||||
|
||||
//** Popover outer arrow width
|
||||
@popover-arrow-outer-width: (@popover-arrow-width + 1);
|
||||
//** Popover outer arrow color
|
||||
@popover-arrow-outer-color: fadein(@popover-border-color, 7.5%);
|
||||
//** Popover outer arrow fallback color
|
||||
@popover-arrow-outer-fallback-color: darken(@popover-fallback-border-color, 20%);
|
||||
|
||||
|
||||
//== Labels
|
||||
//
|
||||
//##
|
||||
|
||||
//** Default label background color
|
||||
@label-default-bg: @gray-light;
|
||||
//** Primary label background color
|
||||
@label-primary-bg: @brand-primary;
|
||||
//** Success label background color
|
||||
@label-success-bg: @brand-success;
|
||||
//** Info label background color
|
||||
@label-info-bg: @brand-info;
|
||||
//** Warning label background color
|
||||
@label-warning-bg: @brand-warning;
|
||||
//** Danger label background color
|
||||
@label-danger-bg: @brand-danger;
|
||||
|
||||
//** Default label text color
|
||||
@label-color: #fff;
|
||||
//** Default text color of a linked label
|
||||
@label-link-hover-color: #fff;
|
||||
|
||||
|
||||
//== Modals
|
||||
//
|
||||
//##
|
||||
|
||||
//** Padding applied to the modal body
|
||||
@modal-inner-padding: 15px;
|
||||
|
||||
//** Padding applied to the modal title
|
||||
@modal-title-padding: 15px;
|
||||
//** Modal title line-height
|
||||
@modal-title-line-height: @line-height-base;
|
||||
|
||||
//** Background color of modal content area
|
||||
@modal-content-bg: #fff;
|
||||
//** Modal content border color
|
||||
@modal-content-border-color: transparent;
|
||||
//** Modal content border color **for IE8**
|
||||
@modal-content-fallback-border-color: #999;
|
||||
|
||||
//** Modal backdrop background color
|
||||
@modal-backdrop-bg: #000;
|
||||
//** Modal backdrop opacity
|
||||
@modal-backdrop-opacity: .5;
|
||||
//** Modal header border color
|
||||
@modal-header-border-color: transparent;
|
||||
//** Modal footer border color
|
||||
@modal-footer-border-color: @modal-header-border-color;
|
||||
|
||||
@modal-lg: 900px;
|
||||
@modal-md: 600px;
|
||||
@modal-sm: 300px;
|
||||
|
||||
|
||||
//== Alerts
|
||||
//
|
||||
//## Define alert colors, border radius, and padding.
|
||||
|
||||
@alert-padding: 15px;
|
||||
@alert-border-radius: @border-radius-base;
|
||||
@alert-link-font-weight: bold;
|
||||
|
||||
@alert-success-bg: @state-success-bg;
|
||||
@alert-success-text: @state-success-text;
|
||||
@alert-success-border: @state-success-border;
|
||||
|
||||
@alert-info-bg: @state-info-bg;
|
||||
@alert-info-text: @state-info-text;
|
||||
@alert-info-border: @state-info-border;
|
||||
|
||||
@alert-warning-bg: @state-warning-bg;
|
||||
@alert-warning-text: @state-warning-text;
|
||||
@alert-warning-border: @state-warning-border;
|
||||
|
||||
@alert-danger-bg: @state-danger-bg;
|
||||
@alert-danger-text: @state-danger-text;
|
||||
@alert-danger-border: @state-danger-border;
|
||||
|
||||
|
||||
//== Progress bars
|
||||
//
|
||||
//##
|
||||
|
||||
//** Background color of the whole progress component
|
||||
@progress-bg: #f5f5f5;
|
||||
//** Progress bar text color
|
||||
@progress-bar-color: #fff;
|
||||
//** Variable for setting rounded corners on progress bar.
|
||||
@progress-border-radius: @border-radius-base;
|
||||
|
||||
//** Default progress bar color
|
||||
@progress-bar-bg: @brand-primary;
|
||||
//** Success progress bar color
|
||||
@progress-bar-success-bg: @brand-success;
|
||||
//** Warning progress bar color
|
||||
@progress-bar-warning-bg: @brand-warning;
|
||||
//** Danger progress bar color
|
||||
@progress-bar-danger-bg: @brand-danger;
|
||||
//** Info progress bar color
|
||||
@progress-bar-info-bg: @brand-info;
|
||||
|
||||
|
||||
//== List group
|
||||
//
|
||||
//##
|
||||
|
||||
//** Background color on `.list-group-item`
|
||||
@list-group-bg: #fff;
|
||||
//** `.list-group-item` border color
|
||||
@list-group-border: #ddd;
|
||||
//** List group border radius
|
||||
@list-group-border-radius: @border-radius-base;
|
||||
|
||||
//** Background color of single list items on hover
|
||||
@list-group-hover-bg: #f5f5f5;
|
||||
//** Text color of active list items
|
||||
@list-group-active-color: @component-active-color;
|
||||
//** Background color of active list items
|
||||
@list-group-active-bg: @component-active-bg;
|
||||
//** Border color of active list elements
|
||||
@list-group-active-border: @list-group-active-bg;
|
||||
//** Text color for content within active list items
|
||||
@list-group-active-text-color: lighten(@list-group-active-bg, 40%);
|
||||
|
||||
//** Text color of disabled list items
|
||||
@list-group-disabled-color: @gray-light;
|
||||
//** Background color of disabled list items
|
||||
@list-group-disabled-bg: @gray-lighter;
|
||||
//** Text color for content within disabled list items
|
||||
@list-group-disabled-text-color: @list-group-disabled-color;
|
||||
|
||||
@list-group-link-color: #555;
|
||||
@list-group-link-hover-color: @list-group-link-color;
|
||||
@list-group-link-heading-color: #333;
|
||||
|
||||
|
||||
//== Panels
|
||||
//
|
||||
//##
|
||||
|
||||
@panel-bg: #fff;
|
||||
@panel-body-padding: 15px;
|
||||
@panel-heading-padding: 10px 15px;
|
||||
@panel-footer-padding: @panel-heading-padding;
|
||||
@panel-border-radius: @border-radius-base;
|
||||
|
||||
//** Border color for elements within panels
|
||||
@panel-inner-border: #ddd;
|
||||
@panel-footer-bg: #f5f5f5;
|
||||
|
||||
@panel-default-text: @gray-dark;
|
||||
@panel-default-border: #ddd;
|
||||
@panel-default-heading-bg: #f5f5f5;
|
||||
|
||||
@panel-primary-text: #fff;
|
||||
@panel-primary-border: @brand-primary;
|
||||
@panel-primary-heading-bg: @brand-primary;
|
||||
|
||||
@panel-success-text: #fff;
|
||||
@panel-success-border: @state-success-border;
|
||||
@panel-success-heading-bg: @brand-success;
|
||||
|
||||
@panel-info-text: #fff;
|
||||
@panel-info-border: @state-info-border;
|
||||
@panel-info-heading-bg: @brand-info;
|
||||
|
||||
@panel-warning-text: #fff;
|
||||
@panel-warning-border: @state-warning-border;
|
||||
@panel-warning-heading-bg: @brand-warning;
|
||||
|
||||
@panel-danger-text: #fff;
|
||||
@panel-danger-border: @state-danger-border;
|
||||
@panel-danger-heading-bg: @brand-danger;
|
||||
|
||||
|
||||
//== Thumbnails
|
||||
//
|
||||
//##
|
||||
|
||||
//** Padding around the thumbnail image
|
||||
@thumbnail-padding: 4px;
|
||||
//** Thumbnail background color
|
||||
@thumbnail-bg: @body-bg;
|
||||
//** Thumbnail border color
|
||||
@thumbnail-border: #ddd;
|
||||
//** Thumbnail border radius
|
||||
@thumbnail-border-radius: @border-radius-base;
|
||||
|
||||
//** Custom text color for thumbnail captions
|
||||
@thumbnail-caption-color: @text-color;
|
||||
//** Padding around the thumbnail caption
|
||||
@thumbnail-caption-padding: 9px;
|
||||
|
||||
|
||||
//== Wells
|
||||
//
|
||||
//##
|
||||
|
||||
@well-bg: #f9f9f9;
|
||||
@well-border: transparent;
|
||||
|
||||
|
||||
//== Badges
|
||||
//
|
||||
//##
|
||||
|
||||
@badge-color: #fff;
|
||||
//** Linked badge text color on hover
|
||||
@badge-link-hover-color: #fff;
|
||||
@badge-bg: @gray-light;
|
||||
|
||||
//** Badge text color in active nav link
|
||||
@badge-active-color: @link-color;
|
||||
//** Badge background color in active nav link
|
||||
@badge-active-bg: #fff;
|
||||
|
||||
@badge-font-weight: normal;
|
||||
@badge-line-height: 1;
|
||||
@badge-border-radius: 10px;
|
||||
|
||||
|
||||
//== Breadcrumbs
|
||||
//
|
||||
//##
|
||||
|
||||
@breadcrumb-padding-vertical: 8px;
|
||||
@breadcrumb-padding-horizontal: 15px;
|
||||
//** Breadcrumb background color
|
||||
@breadcrumb-bg: #f5f5f5;
|
||||
//** Breadcrumb text color
|
||||
@breadcrumb-color: #ccc;
|
||||
//** Text color of current page in the breadcrumb
|
||||
@breadcrumb-active-color: @gray-light;
|
||||
//** Textual separator for between breadcrumb elements
|
||||
@breadcrumb-separator: "/";
|
||||
|
||||
|
||||
//== Carousel
|
||||
//
|
||||
//##
|
||||
|
||||
@carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6);
|
||||
|
||||
@carousel-control-color: #fff;
|
||||
@carousel-control-width: 15%;
|
||||
@carousel-control-opacity: .5;
|
||||
@carousel-control-font-size: 20px;
|
||||
|
||||
@carousel-indicator-active-bg: #fff;
|
||||
@carousel-indicator-border-color: #fff;
|
||||
|
||||
@carousel-caption-color: #fff;
|
||||
|
||||
|
||||
//== Close
|
||||
//
|
||||
//##
|
||||
|
||||
@close-font-weight: normal;
|
||||
@close-color: #000;
|
||||
@close-text-shadow: none;
|
||||
|
||||
|
||||
//== Code
|
||||
//
|
||||
//##
|
||||
|
||||
@code-color: #c7254e;
|
||||
@code-bg: #f9f2f4;
|
||||
|
||||
@kbd-color: #fff;
|
||||
@kbd-bg: #333;
|
||||
|
||||
@pre-bg: #f5f5f5;
|
||||
@pre-color: @gray-dark;
|
||||
@pre-border-color: #ccc;
|
||||
@pre-scrollable-max-height: 340px;
|
||||
|
||||
|
||||
//== Type
|
||||
//
|
||||
//##
|
||||
|
||||
//** Horizontal offset for forms and lists.
|
||||
@component-offset-horizontal: 180px;
|
||||
//** Text muted color
|
||||
@text-muted: @gray-light;
|
||||
//** Abbreviations and acronyms border color
|
||||
@abbr-border-color: @gray-light;
|
||||
//** Headings small color
|
||||
@headings-small-color: @gray-light;
|
||||
//** Blockquote small color
|
||||
@blockquote-small-color: @gray-light;
|
||||
//** Blockquote font size
|
||||
@blockquote-font-size: (@font-size-base * 1.25);
|
||||
//** Blockquote border color
|
||||
@blockquote-border-color: @gray-lighter;
|
||||
//** Page header border color
|
||||
@page-header-border-color: @gray-lighter;
|
||||
//** Width of horizontal description list titles
|
||||
@dl-horizontal-offset: @component-offset-horizontal;
|
||||
//** Point at which .dl-horizontal becomes horizontal
|
||||
@dl-horizontal-breakpoint: @grid-float-breakpoint;
|
||||
//** Horizontal line color.
|
||||
@hr-border: @gray-lighter;
|
||||
@@ -1,41 +0,0 @@
|
||||
.topsql {
|
||||
height: 250px;
|
||||
}
|
||||
.dataTables_filter {
|
||||
padding-top: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.bordered {
|
||||
padding: 0px 0px;
|
||||
border: 1px solid grey;
|
||||
border-radius: 5px;
|
||||
background-color: #EEE;
|
||||
}
|
||||
div.alert {
|
||||
padding: 5px;
|
||||
margin: 0px;
|
||||
}
|
||||
.metadata {
|
||||
overflow: auto;
|
||||
width: 300px;
|
||||
height: 100px;
|
||||
}
|
||||
.fillup {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.fillheight {
|
||||
height: 100%;
|
||||
}
|
||||
.interactions {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
#results {
|
||||
overflow: auto;
|
||||
font-size: 12px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
table tbody tr td {
|
||||
padding: 1px 4px;
|
||||
font-size: small;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
.table i {
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
img.loading {
|
||||
width: 25px;
|
||||
}
|
||||
|
||||
.welcome table {
|
||||
display: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input {
|
||||
margin-left: 5px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
overflow: auto;
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
.parcoords svg, .parcoords canvas {
|
||||
font-size: 12px;
|
||||
position: absolute;
|
||||
}
|
||||
.parcoords > canvas {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.parcoords text.label {
|
||||
font: 100%;
|
||||
font-size: 12px;
|
||||
cursor: drag;
|
||||
}
|
||||
|
||||
.parcoords rect.background {
|
||||
fill: transparent;
|
||||
}
|
||||
.parcoords rect.background:hover {
|
||||
fill: rgba(120,120,120,0.2);
|
||||
}
|
||||
.parcoords .resize rect {
|
||||
fill: rgba(0,0,0,0.1);
|
||||
}
|
||||
.parcoords rect.extent {
|
||||
fill: rgba(255,255,255,0.25);
|
||||
stroke: rgba(0,0,0,0.6);
|
||||
}
|
||||
.parcoords .axis line, .parcoords .axis path {
|
||||
fill: none;
|
||||
stroke: #222;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
.parcoords canvas {
|
||||
opacity: 1;
|
||||
-moz-transition: opacity 0.3s;
|
||||
-webkit-transition: opacity 0.3s;
|
||||
-o-transition: opacity 0.3s;
|
||||
}
|
||||
.parcoords canvas.faded {
|
||||
opacity: 0.25;
|
||||
}
|
||||
.parcoords {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
/* data table styles */
|
||||
.parcoords .row, .parcoords .header {
|
||||
clear: left; font-size: 12px; line-height: 18px; height: 18px;
|
||||
margin: 0px;
|
||||
}
|
||||
.parcoords .row:nth-child(odd) {
|
||||
background: rgba(0,0,0,0.05);
|
||||
}
|
||||
.parcoords .header {
|
||||
font-weight: bold;
|
||||
}
|
||||
.parcoords .cell {
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
width: 100px; height: 18px;
|
||||
}
|
||||
.parcoords .col-0 {
|
||||
width: 180px;
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
// from http://bl.ocks.org/3687826
|
||||
module.exports = function(config) {
|
||||
var columns = [];
|
||||
|
||||
var dg = function(selection) {
|
||||
if (columns.length == 0) columns = d3.keys(selection.data()[0][0]);
|
||||
|
||||
// header
|
||||
selection.selectAll(".header")
|
||||
.data([true])
|
||||
.enter().append("div")
|
||||
.attr("class", "header")
|
||||
|
||||
var header = selection.select(".header")
|
||||
.selectAll(".cell")
|
||||
.data(columns);
|
||||
|
||||
header.enter().append("div")
|
||||
.attr("class", function(d,i) { return "col-" + i; })
|
||||
.classed("cell", true)
|
||||
|
||||
selection.selectAll(".header .cell")
|
||||
.text(function(d) { return d; });
|
||||
|
||||
header.exit().remove();
|
||||
|
||||
// rows
|
||||
var rows = selection.selectAll(".row")
|
||||
.data(function(d) { return d; })
|
||||
|
||||
rows.enter().append("div")
|
||||
.attr("class", "row")
|
||||
|
||||
rows.exit().remove();
|
||||
|
||||
var cells = selection.selectAll(".row").selectAll(".cell")
|
||||
.data(function(d) { return columns.map(function(col){return d[col];}) })
|
||||
|
||||
// cells
|
||||
cells.enter().append("div")
|
||||
.attr("class", function(d,i) { return "col-" + i; })
|
||||
.classed("cell", true)
|
||||
|
||||
cells.exit().remove();
|
||||
|
||||
selection.selectAll(".cell")
|
||||
.text(function(d) { return d; });
|
||||
|
||||
return dg;
|
||||
};
|
||||
|
||||
dg.columns = function(_) {
|
||||
if (!arguments.length) return columns;
|
||||
columns = _;
|
||||
return this;
|
||||
};
|
||||
|
||||
return dg;
|
||||
};
|
||||
62
caravel/assets/vendor/pygments.css
vendored
@@ -1,62 +0,0 @@
|
||||
.codehilite .hll { background-color: #ffffcc }
|
||||
.codehilite { background: #f8f8f8; }
|
||||
.codehilite .c { color: #408080; font-style: italic } /* Comment */
|
||||
.codehilite .err { border: 1px solid #FF0000 } /* Error */
|
||||
.codehilite .k { color: #008000; font-weight: bold } /* Keyword */
|
||||
.codehilite .o { color: #666666 } /* Operator */
|
||||
.codehilite .cm { color: #408080; font-style: italic } /* Comment.Multiline */
|
||||
.codehilite .cp { color: #BC7A00 } /* Comment.Preproc */
|
||||
.codehilite .c1 { color: #408080; font-style: italic } /* Comment.Single */
|
||||
.codehilite .cs { color: #408080; font-style: italic } /* Comment.Special */
|
||||
.codehilite .gd { color: #A00000 } /* Generic.Deleted */
|
||||
.codehilite .ge { font-style: italic } /* Generic.Emph */
|
||||
.codehilite .gr { color: #FF0000 } /* Generic.Error */
|
||||
.codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */
|
||||
.codehilite .gi { color: #00A000 } /* Generic.Inserted */
|
||||
.codehilite .go { color: #808080 } /* Generic.Output */
|
||||
.codehilite .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
|
||||
.codehilite .gs { font-weight: bold } /* Generic.Strong */
|
||||
.codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
|
||||
.codehilite .gt { color: #0040D0 } /* Generic.Traceback */
|
||||
.codehilite .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
|
||||
.codehilite .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
|
||||
.codehilite .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
|
||||
.codehilite .kp { color: #008000 } /* Keyword.Pseudo */
|
||||
.codehilite .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
|
||||
.codehilite .kt { color: #B00040 } /* Keyword.Type */
|
||||
.codehilite .m { color: #666666 } /* Literal.Number */
|
||||
.codehilite .s { color: #BA2121 } /* Literal.String */
|
||||
.codehilite .na { color: #7D9029 } /* Name.Attribute */
|
||||
.codehilite .nb { color: #008000 } /* Name.Builtin */
|
||||
.codehilite .nc { color: #0000FF; font-weight: bold } /* Name.Class */
|
||||
.codehilite .no { color: #880000 } /* Name.Constant */
|
||||
.codehilite .nd { color: #AA22FF } /* Name.Decorator */
|
||||
.codehilite .ni { color: #999999; font-weight: bold } /* Name.Entity */
|
||||
.codehilite .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
|
||||
.codehilite .nf { color: #0000FF } /* Name.Function */
|
||||
.codehilite .nl { color: #A0A000 } /* Name.Label */
|
||||
.codehilite .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
|
||||
.codehilite .nt { color: #008000; font-weight: bold } /* Name.Tag */
|
||||
.codehilite .nv { color: #19177C } /* Name.Variable */
|
||||
.codehilite .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
|
||||
.codehilite .w { color: #bbbbbb } /* Text.Whitespace */
|
||||
.codehilite .mf { color: #666666 } /* Literal.Number.Float */
|
||||
.codehilite .mh { color: #666666 } /* Literal.Number.Hex */
|
||||
.codehilite .mi { color: #666666 } /* Literal.Number.Integer */
|
||||
.codehilite .mo { color: #666666 } /* Literal.Number.Oct */
|
||||
.codehilite .sb { color: #BA2121 } /* Literal.String.Backtick */
|
||||
.codehilite .sc { color: #BA2121 } /* Literal.String.Char */
|
||||
.codehilite .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
|
||||
.codehilite .s2 { color: #BA2121 } /* Literal.String.Double */
|
||||
.codehilite .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
|
||||
.codehilite .sh { color: #BA2121 } /* Literal.String.Heredoc */
|
||||
.codehilite .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
|
||||
.codehilite .sx { color: #008000 } /* Literal.String.Other */
|
||||
.codehilite .sr { color: #BB6688 } /* Literal.String.Regex */
|
||||
.codehilite .s1 { color: #BA2121 } /* Literal.String.Single */
|
||||
.codehilite .ss { color: #19177C } /* Literal.String.Symbol */
|
||||
.codehilite .bp { color: #008000 } /* Name.Builtin.Pseudo */
|
||||
.codehilite .vc { color: #19177C } /* Name.Variable.Class */
|
||||
.codehilite .vg { color: #19177C } /* Name.Variable.Global */
|
||||
.codehilite .vi { color: #19177C } /* Name.Variable.Instance */
|
||||
.codehilite .il { color: #666666 } /* Literal.Number.Integer.Long */
|
||||
146
caravel/assets/vendor/select2.sortable.js
vendored
@@ -1,146 +0,0 @@
|
||||
/**
|
||||
* jQuery Select2 Sortable
|
||||
* - enable select2 to be sortable via normal select element
|
||||
*
|
||||
* author : Vafour
|
||||
* modified : Kevin Provance (kprovance)
|
||||
* inspired by : jQuery Chosen Sortable (https://github.com/mrhenry/jquery-chosen-sortable)
|
||||
* License : GPL
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
$.fn.extend({
|
||||
select2SortableOrder: function () {
|
||||
var $this = this.filter('[multiple]');
|
||||
|
||||
$this.each(function () {
|
||||
var $select = $(this);
|
||||
|
||||
// skip elements not select2-ed
|
||||
if (typeof ($select.data('select2')) !== 'object') {
|
||||
return false;
|
||||
}
|
||||
|
||||
var $select2 = $select.siblings('.select2-container');
|
||||
var sorted;
|
||||
|
||||
// Opt group names
|
||||
var optArr = [];
|
||||
|
||||
$select.find('optgroup').each(function(idx, val) {
|
||||
optArr.push (val);
|
||||
});
|
||||
|
||||
$select.find('option').each(function(idx, val) {
|
||||
var groupName = $(this).parent('optgroup').prop('label');
|
||||
var optVal = this;
|
||||
|
||||
if (groupName === undefined) {
|
||||
if (this.value !== '' && !this.selected) {
|
||||
optArr.push (optVal);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
sorted = $($select2.find('.select2-choices li[class!="select2-search-field"]').map(function () {
|
||||
if (!this) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var id = $(this).data('select2Data').id;
|
||||
|
||||
return $select.find('option[value="' + id + '"]')[0];
|
||||
}));
|
||||
|
||||
sorted.push.apply(sorted, optArr);
|
||||
|
||||
$select.children().remove();
|
||||
$select.append(sorted);
|
||||
});
|
||||
|
||||
return $this;
|
||||
},
|
||||
|
||||
select2Sortable: function () {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
var $this = this.filter('[multiple]'),
|
||||
validMethods = ['destroy'];
|
||||
|
||||
if (args.length === 0 || typeof (args[0]) === 'object') {
|
||||
var defaultOptions = {
|
||||
bindOrder: 'formSubmit', // or sortableStop
|
||||
sortableOptions: {
|
||||
placeholder: 'ui-state-highlight',
|
||||
items: 'li:not(.select2-search-field)',
|
||||
tolerance: 'pointer'
|
||||
}
|
||||
};
|
||||
|
||||
var options = $.extend(defaultOptions, args[0]);
|
||||
|
||||
// Init select2 only if not already initialized to prevent select2 configuration loss
|
||||
if (typeof ($this.data('select2')) !== 'object') {
|
||||
$this.select2();
|
||||
}
|
||||
|
||||
$this.each(function () {
|
||||
var $select = $(this)
|
||||
var $select2choices = $select.siblings('.select2-container').find('.select2-choices');
|
||||
|
||||
// Init jQuery UI Sortable
|
||||
$select2choices.sortable(options.sortableOptions);
|
||||
|
||||
switch (options.bindOrder) {
|
||||
case 'sortableStop':
|
||||
// apply options ordering in sortstop event
|
||||
$select2choices.on("sortstop.select2sortable", function (event, ui) {
|
||||
$select.select2SortableOrder();
|
||||
});
|
||||
|
||||
$select.on('change', function (e) {
|
||||
$(this).select2SortableOrder();
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
// apply options ordering in form submit
|
||||
$select.closest('form').unbind('submit.select2sortable').on('submit.select2sortable', function () {
|
||||
$select.select2SortableOrder();
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (typeof (args[0] === 'string')) {
|
||||
if ($.inArray(args[0], validMethods) == -1) {
|
||||
throw "Unknown method: " + args[0];
|
||||
}
|
||||
|
||||
if (args[0] === 'destroy') {
|
||||
$this.select2SortableDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
},
|
||||
|
||||
select2SortableDestroy: function () {
|
||||
var $this = this.filter('[multiple]');
|
||||
$this.each(function () {
|
||||
var $select = $(this)
|
||||
var $select2choices = $select.parent().find('.select2-choices');
|
||||
|
||||
// unbind form submit event
|
||||
$select.closest('form').unbind('submit.select2sortable');
|
||||
|
||||
// unbind sortstop event
|
||||
$select2choices.unbind("sortstop.select2sortable");
|
||||
|
||||
// destroy select2Sortable
|
||||
$select2choices.sortable('destroy');
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
});
|
||||
}(jQuery));
|
||||
@@ -1,30 +0,0 @@
|
||||
.big_number g.axis text,
|
||||
.big_number_total g.axis text {
|
||||
font-size: 10px;
|
||||
font-weight: normal;
|
||||
color: gray;
|
||||
fill: gray;
|
||||
text-anchor: middle;
|
||||
alignment-baseline: middle;
|
||||
font-weight: none;
|
||||
}
|
||||
|
||||
.big_number text.big,
|
||||
.big_number_total text.big{
|
||||
stroke: black;
|
||||
text-anchor: middle;
|
||||
fill: black;
|
||||
}
|
||||
|
||||
.big_number g.tick line,
|
||||
.big_number_total g.tick line{
|
||||
stroke-width: 1px;
|
||||
stroke: grey;
|
||||
}
|
||||
|
||||
.big_number .domain,
|
||||
.big_number_total .domain{
|
||||
fill: none;
|
||||
stroke: black;
|
||||
stroke-width: 1;
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
// JS
|
||||
var d3 = window.d3 || require('d3');
|
||||
|
||||
// CSS
|
||||
require('./big_number.css');
|
||||
|
||||
var px = require('../javascripts/modules/caravel.js');
|
||||
|
||||
function bigNumberVis(slice) {
|
||||
var div = d3.select(slice.selector);
|
||||
|
||||
function render() {
|
||||
d3.json(slice.jsonEndpoint(), function (error, payload) {
|
||||
//Define the percentage bounds that define color from red to green
|
||||
if (error !== null) {
|
||||
slice.error(error.responseText);
|
||||
return '';
|
||||
}
|
||||
div.html(''); //reset
|
||||
|
||||
var fd = payload.form_data;
|
||||
var json = payload.data;
|
||||
var color_range = [-1, 1];
|
||||
|
||||
var f = d3.format(fd.y_axis_format);
|
||||
var fp = d3.format('+.1%');
|
||||
var width = slice.width();
|
||||
var height = slice.height();
|
||||
div.selectAll("*").remove();
|
||||
var svg = div.append('svg');
|
||||
svg.attr("width", width);
|
||||
svg.attr("height", height);
|
||||
var data = json.data;
|
||||
var compare_suffix = ' ' + json.compare_suffix;
|
||||
var v_compare = null;
|
||||
var v = null;
|
||||
if (fd.viz_type === 'big_number') {
|
||||
v = data[data.length - 1][1];
|
||||
} else {
|
||||
v = data[0][0];
|
||||
}
|
||||
if (json.compare_lag > 0) {
|
||||
var pos = data.length - (json.compare_lag + 1);
|
||||
if (pos >= 0) {
|
||||
v_compare = (v / data[pos][1]) - 1;
|
||||
}
|
||||
}
|
||||
var date_ext = d3.extent(data, function (d) {
|
||||
return d[0];
|
||||
});
|
||||
var value_ext = d3.extent(data, function (d) {
|
||||
return d[1];
|
||||
});
|
||||
|
||||
var margin = 20;
|
||||
var scale_x = d3.time.scale.utc().domain(date_ext).range([margin, width - margin]);
|
||||
var scale_y = d3.scale.linear().domain(value_ext).range([height - (margin), margin]);
|
||||
var colorRange = [d3.hsl(0, 1, 0.3), d3.hsl(120, 1, 0.3)];
|
||||
var scale_color = d3.scale
|
||||
.linear().domain(color_range)
|
||||
.interpolate(d3.interpolateHsl)
|
||||
.range(colorRange).clamp(true);
|
||||
var line = d3.svg.line()
|
||||
.x(function (d) {
|
||||
return scale_x(d[0]);
|
||||
})
|
||||
.y(function (d) {
|
||||
return scale_y(d[1]);
|
||||
})
|
||||
.interpolate("basis");
|
||||
|
||||
var g = svg.append('g');
|
||||
var y = height / 2;
|
||||
//Printing big number
|
||||
g.append("g").attr("class", "digits")
|
||||
.attr('opacity', 1)
|
||||
.append('text')
|
||||
.attr('x', width / 2)
|
||||
.attr('y', y)
|
||||
.attr('class', 'big')
|
||||
.attr('alignment-baseline', 'middle')
|
||||
.attr('id', 'bigNumber')
|
||||
.style('font-weight', 'bold')
|
||||
.style('cursor', 'pointer')
|
||||
.text(f(v))
|
||||
.style('font-size', d3.min([height, width]) / 3.5)
|
||||
.attr('fill', 'white');
|
||||
|
||||
if (fd.viz_type === 'big_number') {
|
||||
//Drawing trend line
|
||||
|
||||
g.append('path')
|
||||
.attr('d', function (d) {
|
||||
return line(data);
|
||||
})
|
||||
.attr('stroke-width', 5)
|
||||
.attr('opacity', 0.5)
|
||||
.attr('fill', "none")
|
||||
.attr('stroke-linecap', "round")
|
||||
.attr('stroke', "grey");
|
||||
|
||||
g = svg.append('g')
|
||||
.attr('class', 'digits')
|
||||
.attr('opacity', 1);
|
||||
|
||||
if (v_compare !== null) {
|
||||
y = (height / 8) * 3;
|
||||
}
|
||||
|
||||
//Printing big number subheader text
|
||||
if (json.subheader !== null) {
|
||||
g.append('text')
|
||||
.attr('x', width / 2)
|
||||
.attr('y', y + d3.min([height, width]) / 4.5)
|
||||
.text(json.subheader)
|
||||
.attr('id', 'subheader_text')
|
||||
.style('font-size', d3.min([height, width]) / 16)
|
||||
.style('text-anchor', 'middle')
|
||||
.attr('fill', c)
|
||||
.attr('stroke', c);
|
||||
}
|
||||
|
||||
var c = scale_color(v_compare);
|
||||
|
||||
//Printing compare %
|
||||
if (v_compare !== null) {
|
||||
g.append('text')
|
||||
.attr('x', width / 2)
|
||||
.attr('y', (height / 16) * 12)
|
||||
.text(fp(v_compare) + compare_suffix)
|
||||
.style('font-size', d3.min([height, width]) / 8)
|
||||
.style('text-anchor', 'middle')
|
||||
.attr('fill', c)
|
||||
.attr('stroke', c);
|
||||
}
|
||||
|
||||
var g_axis = svg.append('g').attr('class', 'axis').attr('opacity', 0);
|
||||
g = g_axis.append('g');
|
||||
var x_axis = d3.svg.axis()
|
||||
.scale(scale_x)
|
||||
.orient('bottom')
|
||||
.ticks(4)
|
||||
.tickFormat(px.formatDate);
|
||||
g.call(x_axis);
|
||||
g.attr('transform', 'translate(0,' + (height - margin) + ')');
|
||||
|
||||
g = g_axis.append('g').attr('transform', 'translate(' + (width - margin) + ',0)');
|
||||
var y_axis = d3.svg.axis()
|
||||
.scale(scale_y)
|
||||
.orient('left')
|
||||
.tickFormat(d3.format(fd.y_axis_format))
|
||||
.tickValues(value_ext);
|
||||
g.call(y_axis);
|
||||
g.selectAll('text')
|
||||
.style('text-anchor', 'end')
|
||||
.attr('y', '-7')
|
||||
.attr('x', '-4');
|
||||
|
||||
g.selectAll("text")
|
||||
.style('font-size', '10px');
|
||||
|
||||
div.on('mouseover', function (d) {
|
||||
var div = d3.select(this);
|
||||
div.selectAll('path').transition().duration(500).attr('opacity', 1)
|
||||
.style('stroke-width', '2px');
|
||||
div.selectAll('g.digits').transition().duration(500).attr('opacity', 0.1);
|
||||
div.selectAll('g.axis').transition().duration(500).attr('opacity', 1);
|
||||
})
|
||||
.on('mouseout', function (d) {
|
||||
var div = d3.select(this);
|
||||
div.select('path').transition().duration(500).attr('opacity', 0.5)
|
||||
.style('stroke-width', '5px');
|
||||
div.selectAll('g.digits').transition().duration(500).attr('opacity', 1);
|
||||
div.selectAll('g.axis').transition().duration(500).attr('opacity', 0);
|
||||
});
|
||||
}
|
||||
slice.done(payload);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
render: render,
|
||||
resize: render
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = bigNumberVis;
|
||||
@@ -1,19 +0,0 @@
|
||||
.directed_force path.link {
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
|
||||
.directed_force circle {
|
||||
fill: #ccc;
|
||||
stroke: #000;
|
||||
stroke-width: 1.5px;
|
||||
stroke-opacity: 1;
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.directed_force text {
|
||||
fill: #000;
|
||||
font: 10px sans-serif;
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
// JS
|
||||
var d3 = window.d3 || require('d3');
|
||||
|
||||
// CSS
|
||||
require('./directed_force.css');
|
||||
|
||||
/* Modified from http://bl.ocks.org/d3noob/5141278 */
|
||||
function directedForceVis(slice) {
|
||||
var div = d3.select(slice.selector);
|
||||
var link_length = slice.data.form_data.link_length || 200;
|
||||
var charge = slice.data.form_data.charge || -500;
|
||||
|
||||
var render = function () {
|
||||
var width = slice.width();
|
||||
var height = slice.height() - 25;
|
||||
d3.json(slice.jsonEndpoint(), function (error, json) {
|
||||
|
||||
if (error !== null) {
|
||||
slice.error(error.responseText);
|
||||
return '';
|
||||
}
|
||||
var links = json.data;
|
||||
var nodes = {};
|
||||
// Compute the distinct nodes from the links.
|
||||
links.forEach(function (link) {
|
||||
link.source = nodes[link.source] || (nodes[link.source] = {
|
||||
name: link.source
|
||||
});
|
||||
link.target = nodes[link.target] || (nodes[link.target] = {
|
||||
name: link.target
|
||||
});
|
||||
link.value = Number(link.value);
|
||||
|
||||
var target_name = link.target.name;
|
||||
var source_name = link.source.name;
|
||||
|
||||
if (nodes[target_name].total === undefined) {
|
||||
nodes[target_name].total = link.value;
|
||||
}
|
||||
if (nodes[source_name].total === undefined) {
|
||||
nodes[source_name].total = 0;
|
||||
}
|
||||
if (nodes[target_name].max === undefined) {
|
||||
nodes[target_name].max = 0;
|
||||
}
|
||||
if (link.value > nodes[target_name].max) {
|
||||
nodes[target_name].max = link.value;
|
||||
}
|
||||
if (nodes[target_name].min === undefined) {
|
||||
nodes[target_name].min = 0;
|
||||
}
|
||||
if (link.value > nodes[target_name].min) {
|
||||
nodes[target_name].min = link.value;
|
||||
}
|
||||
|
||||
nodes[target_name].total += link.value;
|
||||
});
|
||||
|
||||
var force = d3.layout.force()
|
||||
.nodes(d3.values(nodes))
|
||||
.links(links)
|
||||
.size([width, height])
|
||||
.linkDistance(link_length)
|
||||
.charge(charge)
|
||||
.on("tick", tick)
|
||||
.start();
|
||||
|
||||
var svg = div.append("svg")
|
||||
.attr("width", width)
|
||||
.attr("height", height);
|
||||
|
||||
// build the arrow.
|
||||
svg.append("svg:defs").selectAll("marker")
|
||||
.data(["end"]) // Different link/path types can be defined here
|
||||
.enter().append("svg:marker") // This section adds in the arrows
|
||||
.attr("id", String)
|
||||
.attr("viewBox", "0 -5 10 10")
|
||||
.attr("refX", 15)
|
||||
.attr("refY", -1.5)
|
||||
.attr("markerWidth", 6)
|
||||
.attr("markerHeight", 6)
|
||||
.attr("orient", "auto")
|
||||
.append("svg:path")
|
||||
.attr("d", "M0,-5L10,0L0,5");
|
||||
|
||||
var edgeScale = d3.scale.linear()
|
||||
.range([0.1, 0.5]);
|
||||
// add the links and the arrows
|
||||
var path = svg.append("svg:g").selectAll("path")
|
||||
.data(force.links())
|
||||
.enter().append("svg:path")
|
||||
.attr("class", "link")
|
||||
.style("opacity", function (d) {
|
||||
return edgeScale(d.value / d.target.max);
|
||||
})
|
||||
.attr("marker-end", "url(#end)");
|
||||
|
||||
// define the nodes
|
||||
var node = svg.selectAll(".node")
|
||||
.data(force.nodes())
|
||||
.enter().append("g")
|
||||
.attr("class", "node")
|
||||
.on("mouseenter", function (d) {
|
||||
d3.select(this)
|
||||
.select("circle")
|
||||
.transition()
|
||||
.style('stroke-width', 5);
|
||||
|
||||
d3.select(this)
|
||||
.select("text")
|
||||
.transition()
|
||||
.style('font-size', 25);
|
||||
})
|
||||
.on("mouseleave", function (d) {
|
||||
d3.select(this)
|
||||
.select("circle")
|
||||
.transition()
|
||||
.style('stroke-width', 1.5);
|
||||
d3.select(this)
|
||||
.select("text")
|
||||
.transition()
|
||||
.style('font-size', 12);
|
||||
})
|
||||
.call(force.drag);
|
||||
|
||||
// add the nodes
|
||||
var ext = d3.extent(d3.values(nodes), function (d) {
|
||||
return Math.sqrt(d.total);
|
||||
});
|
||||
var circleScale = d3.scale.linear()
|
||||
.domain(ext)
|
||||
.range([3, 30]);
|
||||
|
||||
node.append("circle")
|
||||
.attr("r", function (d) {
|
||||
return circleScale(Math.sqrt(d.total));
|
||||
});
|
||||
|
||||
// add the text
|
||||
node.append("text")
|
||||
.attr("x", 6)
|
||||
.attr("dy", ".35em")
|
||||
.text(function (d) {
|
||||
return d.name;
|
||||
});
|
||||
|
||||
// add the curvy lines
|
||||
function tick() {
|
||||
path.attr("d", function (d) {
|
||||
var dx = d.target.x - d.source.x,
|
||||
dy = d.target.y - d.source.y,
|
||||
dr = Math.sqrt(dx * dx + dy * dy);
|
||||
return "M" +
|
||||
d.source.x + "," +
|
||||
d.source.y + "A" +
|
||||
dr + "," + dr + " 0 0,1 " +
|
||||
d.target.x + "," +
|
||||
d.target.y;
|
||||
});
|
||||
|
||||
node.attr("transform", function (d) {
|
||||
return "translate(" + d.x + "," + d.y + ")";
|
||||
});
|
||||
}
|
||||
|
||||
slice.done(json);
|
||||
});
|
||||
};
|
||||
return {
|
||||
render: render,
|
||||
resize: render
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = directedForceVis;
|
||||
@@ -1,8 +0,0 @@
|
||||
.select2-highlighted > .filter_box {
|
||||
background-color: transparent;
|
||||
border: 1px caravel black;
|
||||
}
|
||||
|
||||
.dashboard .filter_box .slice_container > div {
|
||||
padding-top: 0;
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
// JS
|
||||
var $ = window.$ = require('jquery');
|
||||
var jQuery = window.jQuery = $;
|
||||
var d3 = window.d3 || require('d3');
|
||||
|
||||
// CSS
|
||||
require('./filter_box.css');
|
||||
require('../javascripts/caravel-select2.js');
|
||||
|
||||
function filterBox(slice) {
|
||||
var filtersObj = {};
|
||||
var d3token = d3.select(slice.selector);
|
||||
|
||||
var fltChanged = function () {
|
||||
var val = $(this).val();
|
||||
var vals = [];
|
||||
if (val !== '') {
|
||||
vals = val.split(',');
|
||||
}
|
||||
slice.setFilter($(this).attr('name'), vals);
|
||||
};
|
||||
|
||||
var refresh = function () {
|
||||
d3token.selectAll("*").remove();
|
||||
var container = d3token
|
||||
.append('div')
|
||||
.classed('padded', true);
|
||||
|
||||
$.getJSON(slice.jsonEndpoint(), function (payload) {
|
||||
var maxes = {};
|
||||
|
||||
for (var filter in payload.data) {
|
||||
var data = payload.data[filter];
|
||||
maxes[filter] = d3.max(data, function (d) {
|
||||
return d.metric;
|
||||
});
|
||||
var id = 'fltbox__' + filter;
|
||||
|
||||
var div = container.append('div');
|
||||
|
||||
div.append("label").text(filter);
|
||||
|
||||
div.append('div')
|
||||
.attr('name', filter)
|
||||
.classed('form-control', true)
|
||||
.attr('multiple', '')
|
||||
.attr('id', id);
|
||||
|
||||
filtersObj[filter] = $('#' + id).select2({
|
||||
placeholder: "Select [" + filter + ']',
|
||||
containment: 'parent',
|
||||
dropdownAutoWidth: true,
|
||||
data: data,
|
||||
multiple: true,
|
||||
formatResult: select2Formatter
|
||||
})
|
||||
.on('change', fltChanged);
|
||||
}
|
||||
slice.done(payload);
|
||||
|
||||
function select2Formatter(result, container /*, query, escapeMarkup*/) {
|
||||
var perc = Math.round((result.metric / maxes[result.filter]) * 100);
|
||||
var style = 'padding: 2px 5px;';
|
||||
style += "background-image: ";
|
||||
style += "linear-gradient(to right, lightgrey, lightgrey " + perc + "%, rgba(0,0,0,0) " + perc + "%";
|
||||
|
||||
$(container).attr('style', 'padding: 0px; background: white;');
|
||||
$(container).addClass('filter_box');
|
||||
return '<div style="' + style + '"><span>' + result.text + '</span></div>';
|
||||
}
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
slice.error(xhr.responseText);
|
||||
});
|
||||
};
|
||||
return {
|
||||
render: refresh,
|
||||
resize: refresh
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = filterBox;
|
||||
@@ -1,88 +0,0 @@
|
||||
.heatmap .slice_container {
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.heatmap .axis text {
|
||||
font: 10px sans-serif;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
.heatmap .axis path,
|
||||
.heatmap .axis line {
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.heatmap svg {
|
||||
}
|
||||
|
||||
.heatmap canvas, .heatmap img {
|
||||
image-rendering: optimizeSpeed; /* Older versions of FF */
|
||||
image-rendering: -moz-crisp-edges; /* FF 6.0+ */
|
||||
image-rendering: -webkit-optimize-contrast; /* Safari */
|
||||
image-rendering: -o-crisp-edges; /* OS X & Windows Opera (12.02+) */
|
||||
image-rendering: pixelated; /* Awesome future-browsers */
|
||||
-ms-interpolation-mode: nearest-neighbor; /* IE */
|
||||
}
|
||||
|
||||
/* from d3-tip */
|
||||
.d3-tip {
|
||||
line-height: 1;
|
||||
font-weight: bold;
|
||||
padding: 12px;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: #fff;
|
||||
border-radius: 2px;
|
||||
pointer-events: none;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
/* Creates a small triangle extender for the tooltip */
|
||||
.d3-tip:after {
|
||||
box-sizing: border-box;
|
||||
display: inline;
|
||||
font-size: 10px;
|
||||
width: 100%;
|
||||
line-height: 1;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Northward tooltips */
|
||||
.d3-tip.n:after {
|
||||
content: "\25BC";
|
||||
margin: -1px 0 0 0;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Eastward tooltips */
|
||||
.d3-tip.e:after {
|
||||
content: "\25C0";
|
||||
margin: -4px 0 0 0;
|
||||
top: 50%;
|
||||
left: -8px;
|
||||
}
|
||||
|
||||
/* Southward tooltips */
|
||||
.d3-tip.s:after {
|
||||
content: "\25B2";
|
||||
margin: 0 0 1px 0;
|
||||
top: -8px;
|
||||
left: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Westward tooltips */
|
||||
.d3-tip.w:after {
|
||||
content: "\25B6";
|
||||
margin: -4px 0 0 -1px;
|
||||
top: 50%;
|
||||
left: 100%;
|
||||
}
|
||||
@@ -1,234 +0,0 @@
|
||||
// JS
|
||||
var $ = window.$ || require('jquery');
|
||||
var px = window.px || require('../javascripts/modules/caravel.js');
|
||||
var d3 = require('d3');
|
||||
|
||||
d3.tip = require('d3-tip'); //using window.d3 doesn't capture events properly bc of multiple instances
|
||||
|
||||
// CSS
|
||||
require('./heatmap.css');
|
||||
|
||||
// Inspired from http://bl.ocks.org/mbostock/3074470
|
||||
// https://jsfiddle.net/cyril123/h0reyumq/
|
||||
function heatmapVis(slice) {
|
||||
|
||||
function refresh() {
|
||||
var margin = {
|
||||
top: 10,
|
||||
right: 10,
|
||||
bottom: 35,
|
||||
left: 35
|
||||
};
|
||||
|
||||
d3.json(slice.jsonEndpoint(), function (error, payload) {
|
||||
var matrix = {};
|
||||
if (error) {
|
||||
slice.error(error.responseText);
|
||||
return '';
|
||||
}
|
||||
var fd = payload.form_data;
|
||||
var data = payload.data;
|
||||
|
||||
// Dynamically adjusts based on max x / y category lengths
|
||||
function adjustMargins(data, margins) {
|
||||
var pixelsPerCharX = 4.5; // approx, depends on font size
|
||||
var pixelsPerCharY = 6.8; // approx, depends on font size
|
||||
var longestX = 1;
|
||||
var longestY = 1;
|
||||
var datum;
|
||||
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
datum = data[i];
|
||||
longestX = Math.max(longestX, datum.x.length || 1);
|
||||
longestY = Math.max(longestY, datum.y.length || 1);
|
||||
}
|
||||
|
||||
margins.left = Math.ceil(Math.max(margins.left, pixelsPerCharY * longestY));
|
||||
margins.bottom = Math.ceil(Math.max(margins.bottom, pixelsPerCharX * longestX));
|
||||
}
|
||||
|
||||
function ordScale(k, rangeBands, reverse) {
|
||||
if (reverse === undefined) {
|
||||
reverse = false;
|
||||
}
|
||||
var domain = {};
|
||||
$.each(data, function (i, d) {
|
||||
domain[d[k]] = true;
|
||||
});
|
||||
domain = Object.keys(domain).sort(function (a, b) {
|
||||
return b - a;
|
||||
});
|
||||
if (reverse) {
|
||||
domain.reverse();
|
||||
}
|
||||
if (rangeBands === undefined) {
|
||||
return d3.scale.ordinal().domain(domain).range(d3.range(domain.length));
|
||||
} else {
|
||||
return d3.scale.ordinal().domain(domain).rangeBands(rangeBands);
|
||||
}
|
||||
}
|
||||
adjustMargins(data, margin);
|
||||
|
||||
var width = slice.width();
|
||||
var height = slice.height();
|
||||
var hmWidth = width - (margin.left + margin.right);
|
||||
var hmHeight = height - (margin.bottom + margin.top);
|
||||
var fp = d3.format('.3p');
|
||||
|
||||
var xScale = ordScale('x');
|
||||
var yScale = ordScale('y', undefined, true);
|
||||
var xRbScale = ordScale('x', [0, hmWidth]);
|
||||
var yRbScale = ordScale('y', [hmHeight, 0]);
|
||||
var X = 0,
|
||||
Y = 1;
|
||||
var heatmapDim = [xRbScale.domain().length, yRbScale.domain().length];
|
||||
|
||||
var color = px.color.colorScalerFactory(fd.linear_color_scheme);
|
||||
|
||||
var scale = [
|
||||
d3.scale.linear()
|
||||
.domain([0, heatmapDim[X]])
|
||||
.range([0, hmWidth]),
|
||||
d3.scale.linear()
|
||||
.domain([0, heatmapDim[Y]])
|
||||
.range([0, hmHeight])
|
||||
];
|
||||
|
||||
var container = d3.select(slice.selector);
|
||||
|
||||
var canvas = container.append("canvas")
|
||||
.attr("width", heatmapDim[X])
|
||||
.attr("height", heatmapDim[Y])
|
||||
.style("width", hmWidth + "px")
|
||||
.style("height", hmHeight + "px")
|
||||
.style("image-rendering", fd.canvas_image_rendering)
|
||||
.style("left", margin.left + "px")
|
||||
.style("top", margin.top + "px")
|
||||
.style("position", "absolute");
|
||||
|
||||
var svg = container.append("svg")
|
||||
.attr("width", width)
|
||||
.attr("height", height)
|
||||
.style("left", "0px")
|
||||
.style("top", "0px")
|
||||
.style("position", "absolute");
|
||||
|
||||
var rect = svg.append('g')
|
||||
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
|
||||
.append('rect')
|
||||
.style('fill-opacity', 0)
|
||||
.attr('stroke', 'black')
|
||||
.attr("width", hmWidth)
|
||||
.attr("height", hmHeight);
|
||||
|
||||
var tip = d3.tip()
|
||||
.attr('class', 'd3-tip')
|
||||
.offset(function () {
|
||||
var k = d3.mouse(this);
|
||||
var x = k[0] - (hmWidth / 2);
|
||||
return [k[1] - 20, x];
|
||||
})
|
||||
.html(function (d) {
|
||||
var s = "";
|
||||
var k = d3.mouse(this);
|
||||
var m = Math.floor(scale[0].invert(k[0]));
|
||||
var n = Math.floor(scale[1].invert(k[1]));
|
||||
if (m in matrix && n in matrix[m]) {
|
||||
var obj = matrix[m][n];
|
||||
s += "<div><b>" + fd.all_columns_x + ": </b>" + obj.x + "<div>";
|
||||
s += "<div><b>" + fd.all_columns_y + ": </b>" + obj.y + "<div>";
|
||||
s += "<div><b>" + fd.metric + ": </b>" + obj.v + "<div>";
|
||||
s += "<div><b>%: </b>" + fp(obj.perc) + "<div>";
|
||||
tip.style("display", null);
|
||||
} else {
|
||||
// this is a hack to hide the tooltip because we have map it to a single <rect>
|
||||
// d3-tip toggles opacity and calling hide here is undone by the lib after this call
|
||||
tip.style("display", "none");
|
||||
}
|
||||
return s;
|
||||
});
|
||||
|
||||
rect.call(tip);
|
||||
|
||||
var xAxis = d3.svg.axis()
|
||||
.scale(xRbScale)
|
||||
.tickValues(xRbScale.domain().filter(
|
||||
function (d, i) {
|
||||
return !(i % (parseInt(fd.xscale_interval, 10)));
|
||||
}))
|
||||
.orient("bottom");
|
||||
|
||||
var yAxis = d3.svg.axis()
|
||||
.scale(yRbScale)
|
||||
.tickValues(yRbScale.domain().filter(
|
||||
function (d, i) {
|
||||
return !(i % (parseInt(fd.yscale_interval, 10)));
|
||||
}))
|
||||
.orient("left");
|
||||
|
||||
svg.append("g")
|
||||
.attr("class", "x axis")
|
||||
.attr("transform", "translate(" + margin.left + "," + (margin.top + hmHeight) + ")")
|
||||
.call(xAxis)
|
||||
.selectAll("text")
|
||||
.style("text-anchor", "end")
|
||||
.attr("transform", "rotate(-45)");
|
||||
|
||||
svg.append("g")
|
||||
.attr("class", "y axis")
|
||||
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
|
||||
.call(yAxis);
|
||||
|
||||
rect.on('mousemove', tip.show);
|
||||
rect.on('mouseout', tip.hide);
|
||||
|
||||
var context = canvas.node().getContext("2d");
|
||||
context.imageSmoothingEnabled = false;
|
||||
createImageObj();
|
||||
|
||||
// Compute the pixel colors; scaled by CSS.
|
||||
function createImageObj() {
|
||||
var imageObj = new Image();
|
||||
var image = context.createImageData(heatmapDim[0], heatmapDim[1]);
|
||||
var pixs = {};
|
||||
$.each(data, function (i, d) {
|
||||
var c = d3.rgb(color(d.perc));
|
||||
var x = xScale(d.x);
|
||||
var y = yScale(d.y);
|
||||
pixs[x + (y * xScale.domain().length)] = c;
|
||||
if (matrix[x] === undefined) {
|
||||
matrix[x] = {};
|
||||
}
|
||||
if (matrix[x][y] === undefined) {
|
||||
matrix[x][y] = d;
|
||||
}
|
||||
});
|
||||
|
||||
var p = -1;
|
||||
for (var i = 0; i < heatmapDim[0] * heatmapDim[1]; i++) {
|
||||
var c = pixs[i];
|
||||
var alpha = 255;
|
||||
if (c === undefined) {
|
||||
c = d3.rgb('#F00');
|
||||
alpha = 0;
|
||||
}
|
||||
image.data[++p] = c.r;
|
||||
image.data[++p] = c.g;
|
||||
image.data[++p] = c.b;
|
||||
image.data[++p] = alpha;
|
||||
}
|
||||
context.putImageData(image, 0, 0);
|
||||
imageObj.src = canvas.node().toDataURL();
|
||||
}
|
||||
|
||||
slice.done();
|
||||
|
||||
});
|
||||
}
|
||||
return {
|
||||
render: refresh,
|
||||
resize: refresh
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = heatmapVis;
|
||||