Repository: cliqz-oss/whotracks.me Branch: master Commit: 44f3b9290cd0 Files: 179 Total size: 17.9 MB Directory structure: gitextract_rc25zhaz/ ├── .github/ │ └── workflows/ │ └── test.yml ├── .gitignore ├── .tool-versions ├── Dockerfile ├── Jenkinsfile ├── LICENSE.md ├── README.md ├── RIGHT_TO_AMEND.md ├── blog/ │ ├── adblockers_performance_study.md │ ├── block-third-party-cookies.md │ ├── cookie-consent.md │ ├── cookies.md │ ├── dexie_transaction_bug.md │ ├── fingerprinting.md │ ├── gdpr-what-happened.md │ ├── generating_adblocker_filters.md │ ├── google_domains.md │ ├── government_websites_september.md │ ├── how_cliqz_antitracking_protects_users.md │ ├── how_facebook_knows_exactly_what_turns_you_on.md │ ├── manifest_v3_privacy.md │ ├── private_analytics.md │ ├── static_site.md │ ├── static_site_blog.md │ ├── static_site_generation.md │ ├── static_site_visualization.md │ ├── tracker-tax.md │ ├── tracker_categories.md │ ├── trackers-who-steal.md │ ├── trackers_in_your_favorite_site.md │ ├── tracking_and_ux.md │ ├── tracking_pixel.md │ ├── update_apr_2018.md │ ├── update_dec_2017.md │ ├── update_feb_2018.md │ ├── update_jan_2018.md │ ├── update_jun_2018.md │ ├── update_may_2018.md │ ├── updating_our_tracking_prevalence_metrics.md │ ├── what_is_a_tracker.md │ └── where_is_the_data_from.md ├── contrib/ │ ├── generating_adblocker_filters.py │ ├── tracker_map_notebook.ipynb │ ├── wtm_april_update.ipynb │ └── wtm_may_update.ipynb ├── deploy_to_s3.py ├── docs/ │ └── local-build.md ├── pyproject.toml ├── static/ │ ├── font-awesome-4.7.0/ │ │ ├── HELP-US-OUT.txt │ │ ├── css/ │ │ │ └── font-awesome.css │ │ ├── fonts/ │ │ │ └── FontAwesome.otf │ │ ├── less/ │ │ │ ├── animated.less │ │ │ ├── bordered-pulled.less │ │ │ ├── core.less │ │ │ ├── fixed-width.less │ │ │ ├── font-awesome.less │ │ │ ├── icons.less │ │ │ ├── larger.less │ │ │ ├── list.less │ │ │ ├── mixins.less │ │ │ ├── path.less │ │ │ ├── rotated-flipped.less │ │ │ ├── screen-reader.less │ │ │ ├── stacked.less │ │ │ └── variables.less │ │ └── scss/ │ │ ├── _animated.scss │ │ ├── _bordered-pulled.scss │ │ ├── _core.scss │ │ ├── _fixed-width.scss │ │ ├── _icons.scss │ │ ├── _larger.scss │ │ ├── _list.scss │ │ ├── _mixins.scss │ │ ├── _path.scss │ │ ├── _rotated-flipped.scss │ │ ├── _screen-reader.scss │ │ ├── _stacked.scss │ │ ├── _variables.scss │ │ └── font-awesome.scss │ ├── fonts/ │ │ └── RationalTWSemiBold.otf │ ├── js/ │ │ ├── bootstrap.js │ │ ├── d3.layout.cloud.js │ │ ├── explorer.js │ │ ├── ghostery.js │ │ ├── highlight.pack.js │ │ └── search.js │ └── scss/ │ ├── _colors.scss │ ├── blog/ │ │ ├── card.scss │ │ ├── github.scss │ │ └── post.scss │ ├── bootstrap.min.scss │ ├── companies/ │ │ └── reach-chart.scss │ ├── custom.scss │ ├── datatables.colReorder.min.scss │ ├── datatables.min.scss │ ├── explorer/ │ │ └── table.scss │ ├── home/ │ │ └── index.scss │ ├── trackers/ │ │ ├── list.scss │ │ └── profile.scss │ └── websites/ │ ├── overview.scss │ └── profile.scss ├── templates/ │ ├── base.html │ ├── blog-page.html │ ├── blog.html │ ├── company-page.html │ ├── components/ │ │ ├── blog-card.html │ │ ├── breadcrumb.html │ │ ├── category-item.html │ │ ├── company-card.html │ │ ├── cookies.html │ │ ├── fingerprinting.html │ │ ├── footer.html │ │ ├── home/ │ │ │ └── header.html │ │ ├── navbar.html │ │ ├── tag_cloud.html │ │ ├── top-5-info-box.html │ │ ├── top-5-trackers.html │ │ ├── tracker-list.html │ │ ├── trackers/ │ │ │ ├── category.html │ │ │ └── header.html │ │ ├── tracking-methods.html │ │ ├── unified-ui-tracker-list.html │ │ ├── website-list.html │ │ └── websites/ │ │ ├── header.html │ │ └── tracker-list.html │ ├── explorer.html │ ├── imprint.html │ ├── index.html │ ├── not-found.html │ ├── privacy-policy.html │ ├── reach-chart-page.html │ ├── tracker-not-found.html │ ├── tracker-page.html │ ├── trackers.html │ ├── website-not-found.html │ ├── website-page.html │ └── websites.html ├── tests/ │ ├── __init__.py │ ├── test_data_integrity.py │ ├── test_db_integrity.py │ ├── test_db_validity.py │ ├── test_site_categories.py │ └── test_sites_data.py ├── update_trackerdb.sh ├── update_trackers_preview.py └── whotracksme/ ├── __init__.py ├── data/ │ ├── Readme.md │ ├── __init__.py │ ├── assets/ │ │ ├── trackerdb.sql │ │ └── trackers-preview.json │ ├── db.py │ ├── loader.py │ └── pack.py ├── main.py ├── qa/ │ ├── __init__.py │ ├── todo.py │ └── utils.py └── website/ ├── __init__.py ├── api/ │ └── meta.py ├── build/ │ ├── __init__.py │ ├── blog.py │ ├── companies.py │ ├── data.py │ ├── explorer.py │ ├── home.py │ ├── trackers.py │ └── websites.py ├── builder.py ├── plotting/ │ ├── .vscode/ │ │ └── settings.json │ ├── __init__.py │ ├── colors.py │ ├── companies.py │ ├── plots.py │ ├── sankey.py │ ├── trackers.py │ └── utils.py ├── serve.py ├── templates.py └── utils.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/test.yml ================================================ name: Tests on: push: branches: [master] pull_request: branches: [master] jobs: test: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - name: Install sass run: | sudo apt-get update sudo apt-get install --yes ruby-sass build-essential - name: Install uv uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: python-version: '3.13' - name: Install dependencies run: | uv sync --locked uv run whotracksme --help - name: Fetch test data assets run: | aws --no-sign-request s3 cp --recursive s3://data.whotracks.me/2017-06 2017-06 aws --no-sign-request s3 cp --recursive s3://data.whotracks.me/2021-06 2021-06 working-directory: whotracksme/data/assets env: AWS_DEFAULT_REGION: us-east-1 - name: Run tests run: | uv run pytest - name: Check build run: | uv run whotracksme website ================================================ FILE: .gitignore ================================================ *.pyc .cache/ .sass-cache/ __pycache__/ _site/ dist/ whotracksme.egg-info/ .DS_Store venv/ whotracksme/data/assets/**/*.csv whotracksme.db ================================================ FILE: .tool-versions ================================================ python 3.11.6 ================================================ FILE: Dockerfile ================================================ # Set base image to build upon FROM python:3.11-slim # Set arg and env ARG VERSION ARG UID=1000 ARG GID=1000 ARG USER=jenkins ARG GROUP=jenkins # Add jenkins user and group RUN groupadd -g ${GID} ${GROUP} && \ useradd -u ${UID} -g ${GID} -m -s /bin/bash ${USER} # Set labels to identify image LABEL vendor="Ghostery GmbH" \ maintainer="chrmod@ghostery.com" \ version=${VERSION} RUN apt-get update && \ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ build-essential \ libffi-dev \ ruby-sass \ && \ rm -rf /var/lib/apt/lists/* && \ rm -f /var/cache/apt/*.bin # Copy application python requirements COPY requirements-dev.txt /home/jenkins/ # Install python dependencies RUN pip install -r /home/jenkins/requirements-dev.txt ================================================ FILE: Jenkinsfile ================================================ def testReport = 'test-report.xml' def stagingBucket = 'internal.clyqz.com' def stagingPrefix = '/docs/whotracksme' def productionBucket = 'whotracksme' def productionPrefix = '' node('magrathea') { stage ('Checkout') { checkout([ $class: 'GitSCM', branches: [[name: 'refs/heads/'+env.BRANCH_NAME]], extensions: [[$class: 'GitLFSPull']], userRemoteConfigs: [ [refspec: '+refs/heads/*:refs/remotes/origin/* +refs/pull/*/head:refs/remotes/origin/PR-* +refs/tags/*:refs/remotes/origin/*', url: 'https://github.com/ghostery/whotracks.me.git'] ] ]) } def img stage('Download Datasets') { dir('whotracksme/data/assets') { sh('aws s3 sync --no-sign-request --no-progress s3://data.whotracks.me/ .') } } stage('Build Docker Image') { img = docker.build('whotracksme', '. --build-arg user=`whoami` --build-arg UID=`id -u` --build-arg GID=`id -g`') } img.inside() { try { stage('Install') { sh("python -m pip install --user -e '.[dev]'") } stage('Test') { try { sh(script: "pytest --junit-xml=${testReport}") } catch(err) { junit(testReport) currentBuild.result = "FAILURE" } } stage('Build site') { sh('/home/jenkins/.local/bin/whotracksme website') } if (env.BRANCH_NAME == 'master') { withCredentials([[ $class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: '04e892d6-1f78-400e-9908-1e9466e238a9', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY' ]]) { stage('Publish Site') { sh("python deploy_to_s3.py ${productionBucket} ${productionPrefix} --production") } } } } finally { // cleanup sh('rm -rf _site; rm -rf .sass-cache') } } junit(testReport) } ================================================ FILE: LICENSE.md ================================================ MIT License Copyright (c) 2017 - to present Ghostery GmbH Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================  

WhoTracks.Me

Bringing Transparency to Online Tracking

Transparency · Privacy · Tracking landscape · Built by Ghostery
Trackers · Websites · Explorer

powered by Ghostery License Badge

# Downloading the data Each month, we release a new version of the web site. The data from the last month can be directly [accessed through the website](https://www.ghostery.com/whotracksme/explorer). The raw data, from which the graphs have been computed, is also available as an open data set (updated every month). You can also download historical data. More information on the raw data can be found [here](whotracksme/data/Readme.md). WhoTracks.me also builts heavily on another open source project called [TrackerDB](https://github.com/ghostery/trackerdb); all meta data (e.g. company descriptions) is maintained there. # Using the data You can directly use the [raw data](whotracksme/data/Readme.md), which are all text files. As an alternative, you an also download it locally and use the Python API: ``` uv sync --frozen . .venv/bin/activate ``` The Python API can now be accessed as follows (make sure you have already downloaded data): ```python from whotracksme.data.loader import DataSource data = DataSource() # available entities data.trackers data.companies data.sites ``` A whitepaper for WhoTracks.me is available at https://arxiv.org/abs/1804.08959, and here's a BibTeX entry that you can use to cite it in a publication: ``` @misc{whotracksme, title={WhoTracks.Me: Shedding light on the opaque world of online tracking}, author={Arjaldo Karaj and Sam Macbeth and Rémi Berson and Josep M. Pujol}, year={2018}, eprint={1804.08959}, archivePrefix={arXiv}, primaryClass={cs.CY} } ``` # Contributing We rely on contributions for the community to keep the quality of this project high. If you want, you can support us in multiple ways: * Do you see inconsistencies in the data? Please open a Github issue [here](https://github.com/whotracksme/whotracks.me/issues). We will have a look! * Do you see wrong company descriptions? Did we put something in the category? Please check out the [TrackerDB project](https://github.com/ghostery/trackerdb), where all the meta data is kept, and open an [issue](https://github.com/ghostery/trackerdb/issues), or send us a pull request. * Do you have any feedback on the [WhoTracks.me homepage](https://www.ghostery.com/whotracksme) or about the documentation? Please, let us know, so we can improve. You can also contact us via email at [info@whotracks.me](mailto:info@whotracks.me) # Right to Amend Please read our [Guideline for 3rd parties](https://github.com/ghostery/whotracks.me/blob/master/RIGHT_TO_AMEND.md) wanting to suggest corrections to their data. # Local builds [Readme on local builds](docs/local-build.md) (this is mostly relevant for the maintainer of this project) # License The content of this project itself is licensed under the [Creative Commons Attribution 4.0 license](https://creativecommons.org/licenses/by/4.0/), and the underlying source code used to generate and display that content is licensed under the [MIT license](https://github.com/ghostery/whotracks.me/blob/master/LICENSE.md). ================================================ FILE: RIGHT_TO_AMEND.md ================================================ # Right to Amend A Guideline for 3rd parties wanting to suggest corrections to their data whotracks.me has already grown to be the most comprehensive transparency and monitoring tool on online tracking that we are aware of. It already features longitudinal data of more than 800 million page loads. We are constantly working on improving the quality of our database, and at this scale, it is possible to have minor inaccuracies. Hence, we are always happy to amend incorrect entries when pointed out. This is a guideline for 3rd parties wanting to suggest corrections to their data. 1. Please reach out by opening an issue in the repository. 2. Identify yourself, and your role in the organization on behalf of which you are proposing an amendment of the data. 3. Provide urls to the whotracks.me pages that contain inaccuracies (e.g. https://whotracks.me/trackers/name-of-tracker) 4. Clearly list the inaccuracy(y/ies), and for each write a sentence or two providing evidence/justification w.r.t. why the current information is not correct. We dislike inaccurate information just as much as you do, and will do our best to resolve the issue as soon as we can. ================================================ FILE: blog/adblockers_performance_study.md ================================================ title: Adblockers Performance Study subtitle: description: A detailed comparison of popular adblockers with tips to help you block ads effectively and improve your digital privacy. author: type: article publish: True date: 2019-02-15 tags: blog, adblocker, performance header_img: blog/adblocker-perf-study.jpg redirect_url: https://www.ghostery.com/blog/adblockers-performance-study +++ _In this study, we show_ * _That all popular content-blockers are very efficient, having sub-millisecond median decision time per request_ * _That the manifest v3 performance claim is inaccurate based on our measurements_ * _That the ad blocker used by Cliqz and [Ghostery](https://www.ghostery.com/) consistently performs as well or better than other popular content-blockers_ * _How to block ads effectively and improve your digital privacy._ --- ## About the adblocker performance study Here, we present a detailed analysis of the performance of some of the most popular adblockers and content-blocker engines: *uBlock Origin*, *Adblock Plus*, *Brave*, *DuckDuckGo* and *Cliqz/Ghostery's* advanced adblocker (shipped since Ghostery 8), which we will refer to as *Ghostery* for the rest of the article. ## Why was the study conducted? This study was motivated by the recent [Manifest V3 controversy](https://bugs.chromium.org/p/chromium/issues/detail?id=896897). One of the proposed changes involves crippling the WebRequest APIs to limit their blocking abilities. Two justifications were put forth: one related to *performance* and another related to privacy. The privacy argument deserves its own separate analysis and will not be covered here. ### What were the findings? In this study, we show that the *performance* argument does not hold. Our comparison demonstrates that the most popular content-blockers and adblockers are already very efficient (having a sub-millisecond median decision time per request) and should not result in any overhead noticeable by users. We showed in another study [The Tracker Tax](https://www.ghostery.com/lp/trackertax/) that blocking ads and trackers actually reduces the loading time of websites by **up to a factor of 2**. Besides, efficiency is continuously improved and technologies such as WebAssembly will enable further optimizations. ### What did the study compare? This comparison does not involve full extensions, but instead **focuses on network request-blocking engines**. This is the most CPU-intensive task performed by content-blockers (in particular, this does not account for cosmetics engines or subscription management). Here are the home pages for all content-blockers compared: * Ghostery and Cliqz's [adblocker v0.6.9](https://github.com/cliqz-oss/adblocker/commit/58d89689af95d09e02a52e57aceb75151153d4ab). * Brave's [ad-block 4.1.3](https://github.com/brave/ad-block/commit/cfb714387fef649bd4ec7c1242ae442d58e4d41f). * DuckDuckGo's [abp-filter-parser 0.2.0](https://github.com/duckduckgo/abp-filter-parser/commit/01a864e84f472e31b9f5c47bbc05a7d75ee1ca62). * uBlock Origin commit [29b10d2](https://github.com/gorhill/uBlock/commit/29b10d215184aef1a9a12b715b47de9656ecdc3c). * AdblockPlus' [adblockpluscore 34c49bb](https://github.com/adblockplus/adblockpluscore/commit/34c49bbf029e586226220c067c50cec6e8bf8842). We did not include native blockers from Chromium and Safari projects as this would require some significant effort to package them in a way that allows benchmarking against the other libraries. We leave this for future work. ### How were the adblockers compared? Every adblocker, except *uBlock Origin*, are available as JavaScript libraries which can be loaded in Node.js. To allow a comparison of *uBlock Origin*, we had to extract the static network filtering engine [out of the extension](https://github.com/cliqz-oss/adblocker/blob/master/bench/comparison/ublock.js). The version of *uBlock Origin* running in this benchmark *does not make use of the Webassembly* version of domain matching. All benchmarks were run on an X1 Carbon 2016 (i7 U6600 + 16 GB) in Node.js 11.9.0. Memory measurements were performed in Google Chrome version 72.0.3626.96 using the memory snapshot tool. ## Results Before presenting the detailed analysis of the results, let us highlight our findings in a nutshell: - All content-blockers except *DuckDuckGo* have **sub-millisecond median decision time** per request. - **Time to Process a Request in Ghostery** (median): **0.007 ms** - 2.7x faster than *uBlock Origin* - 2.9x faster than *Adblock Plus* - 6.3x faster than *Brave* - 1258.4x faster than *DuckDuckGo* - **Loading Ghostery's Blocking Engine** (from cache): **0.03 ms** - 368x faster than *Brave* - 588x faster than *uBlock Origin* - 3575x faster than *Adblock Plus* - *DuckDuckGo*'s engine does not offer serialization, so the loading cost is always the one from parsing the lists. - **Memory Consumption of Ghostery's Blocking Engine** (at startup, in Chrome): **1.8 MB** - 1.6x less memory than *uBlock Origin* - 8.4x less memory than *Adblock Plus* - 8.8x less memory than *DuckDuckGo* - The memory usage of *Brave* could not be evaluated using the devtools and thus is not included in this section. ### 0. About the Dataset To measure the performance of each adblocker, we replayed requests from popular domains and tracked the time it took to decide if they should be blocked or not. We then analyzed the results in three different ways: all requests, blocked only and not blocked (taken from the same run). ### How the dataset was created The dataset was created using a pool of Chrome headless browsers (driven by the [`puppeteer` library](https://github.com/GoogleChrome/puppeteer)) to visit home pages of the *top 500 domains* (as reported by Cliqz Search). Up to 3 pages of each domain (picked randomly from the home page) and all the network requests seen (URL, frame URL and type) were also collected. The dataset was shuffled in such a way that the different pages were visited in a random order, but requests seen on each page were replayed as they were recorded initially. In summary: * The dataset is composed of 242944 requests. * We released the data publicly at this URL: [requests_top500.json.gz](https://cdn.cliqz.com/adblocking/requests_top500.json.gz). * The script to create the dataset is also available: [create_dataset.js](https://github.com/cliqz-oss/adblocker/blob/master/bench/comparison/create_dataset.js) and [shuffle_dataset.js](https://github.com/cliqz-oss/adblocker/blob/master/bench/comparison/shuffle_dataset.js) was used to shuffle the requests to produce the final data. ### 1. Composition of Requests For the purpose of this comparison, we consider that each network request can be either blocked or allowed by the content-blocker; we call the process of deciding whether a request should be blocked or not: *matching*. We observed that from our dataset, only ~19.2% are blocked (average across all content-blockers). Composition of requests ### Key takeaway: how to make an adblocker more effective This observation suggests that content-blockers will perform better on average if they can efficiently decide which requests to *not block*. The filters used to determine whether or not a request is to be blocked are the ones from [Easylist](https://easylist-downloads.adblockplus.org/easylist.txt), where we removed all the cosmetic rules before running the benchmarks. The final list contains *38978 network filters* and is available here: [easylist.txt](https://github.com/cliqz-oss/adblocker/blob/master/bench/comparison/easylist.txt). It should be noted at this point that a larger proportion of requests would be blocked by enabling extra filters lists such as *EasyPrivacy*. ### 2. Time To Match All Requests We first look at all of the requests (whether they will eventually be blocked or not). We use a log scale for the x-axis (time in milliseconds) to facilitate the comparison of the cumulative distribution of the time it takes for adblockers to decide whether or not a request should be blocked. Here is a break-down of the 99th percentile and median times for each content-blocker:
99% OF REQUESTS MEDIAN
Ghostery 0.050ms 0.007ms
uBlock Origin 0.124ms (2.5x slower) 0.017ms (2.7x slower)
Adblock Plus 0.103ms (2.1x slower) 0.019ms (2.9x slower)
Brave 1.288ms (25.9x slower) 0.041ms (6.3x slower)
DuckDuckGo 12.085ms (242.5x slower) 8.270ms (1258.4x slower)
Below you can find the cumulative distribution plots of these timings: Time To Match All Requests ### 3. Time To Match Requests Which Are Not Blocked The following table details 99th percentile and median timings for requests not blocked:
99% OF REQUESTS MEDIAN
Ghostery 0.049ms 0.006ms
uBlock Origin 0.112ms (2.3x slower) 0.018ms (2.8x slower)
Adblock Plus 0.105ms (2.2x slower) 0.020ms (3.1x slower)
Brave 1.270ms (26.2x slower) 0.038ms (5.9x slower)
DuckDuckGo 11.190ms (230.5x slower) 6.781ms (1060.5x slower)
Time to match requests which are not blocked ### 4. Time To Match Requests Which Are Blocked The following table details 99th percentile and median timings for requests blocked:
99% OF REQUESTS MEDIAN
Ghostery 0.052ms 0.007ms
uBlock Origin 0.165ms (3.1x slower) 0.016ms (2.2x slower)
Adblock Plus 0.099ms (1.9x slower) 0.014ms (1.9x slower)
Brave 1.468ms (28.0x slower) 0.062ms (8.5x slower)
DuckDuckGo 13.025ms (248.5x slower) 8.31ms (1130.6x slower)
Time to match requests which are blocked ### Summary of findings: How the adblockers performed On these graphs, we observe a plateau for *Adblock Plus*, *Brave* and *Duckduckgo*. This can be explained by the fact that these engines implement some form of caching internally, thus having a very fast response time for some requests (redundancy in requests comes from both common third parties seen on multiple websites as well as the fact that we load several pages for each domain). This caching can be implemented on top of any content-blocker and does not tell much about the efficiency of each. We can see this as a means to trade *memory* against *CPU usage*. ### Ghostery’s adblocker performance From the previous measurements, we see that Ghostery outperforms other libraries in terms of matching speed. Without going into too many details, here are some of the optimizations which can explain these results: * Ghostery makes use of a reverse index associating tokens to filters. Contrary to other libraries, we make sure that we pick *the best* token for each filter at construction time (best being defined as the *least seen token*). This incurs a one-time extra cost but results in maximized dispatching capabilities. * Filters are stored in a very compact form, in typed arrays, and only loaded in memory lazily, when there is a chance they will be blocked (if we encounter identical tokens in URLs). * Filters loaded in memory are optimized on-the-fly and multiple filters can be combined for increased efficiency. The optimizations were carefully crafted based on common cases observed in Easylist. ### 5. Serialization And Deserialization In this section, we have a look at the performance of content-blockers when it comes to serializing their internal representation for faster subsequent loading. Only *DuckDuckGo*'s engine does not provide this feature. *uBlock Origin*, *Ghostery*, *Adblock Plus* and *Brave* all allow to serialize or cache (*uBlock Origin*'s terminology is: *selfies*) the entire blocking engine to either a string or a buffer, which can then be used to speed-up subsequent loads. ### The impact of load time on user experience As this is a one-time operation, having a higher loading time does not impact desktop users significantly. On the other hand, the ability to quickly initialize the content-blocker is critical on mobile. ### Comparison of serialization and deserialization times Another use-case allowed by such capability is to perform the parsing of the lists on the backend and ship the serialized form of the content-blocker to clients directly. This removes the cost of initialization completely. We performed 100 serializations for each content-blocker and display the results below: Serialization timings This bar plot contains the median time taken to serialize the engine for each content-blocker: Serialization timings Similarly, we measure the time it takes to restore the content-blocker from its serialized form: Deserialization timings And here is the median time: Deserialization timings Last but not least, we measured the size of the serialized buffer for each content-blocker: Cache size From these measurements, we see that *Ghostery* offers both significantly faster serialization and deserialization times as well as a smaller cache size. The reason is the following: * The internal representation is already mostly stored in a compact form (using typed arrays). * This means that serialization only adds a small amount of metadata alongside the already available arrays. Deserialization is *essentially instantaneous* since it's enough to create some typed array views on top of the serialized buffer (think of `mmap` but using typed arrays). * This also explains the very low memory consumption: after initialization, the memory footprint is only slightly higher than the size of the serialized form. ### 6. Memory Consumption at Start-up Here, we consider the memory usage of each content-blocker, initialized from lists (not from cache) after one full garbage collection. ### How memory consumption was measured The measurements were performed using Chrome's devtools memory snapshot. We did not measure *Brave* here since the memory used from C++ side does not seem to be taken into account in the snapshot. Keep in mind that this memory usage can vary at run-time as content-blockers might cache frequently used resources, etc. Memory usage at start-up As mentioned in the previous section on serialization, the very low memory usage of *Ghostery* can be explained by the fact that the internal representation mostly consists of very compact typed arrays with some small overhead for extra meta-data. Again, we need to stress here that this measures the network filtering engine of Ghostery only, not the full extension, as described in the introduction. ### 7. Parsing Lists In this graph, we present the time it takes for each content-blocker to be initialized from the lists (without any prior caching, which means initializing all internal resources by parsing the raw list). We see that only *Brave* seems to be significantly slower and that *uBlock Origin*, *Ghostery*, *Adblock Plus* and *DuckDuckGo* all perform well. Time to parse Easylist It seems that the long parsing time for *Brave* is a [known issue](https://github.com/brave/ad-block/issues/158) tracked on their GitHub repository. ### Key findings on parsing lists If we remove *Brave*, we see that there are still differences between *uBlock Origin*, *Ghostery*, *Adblock Plus* and *DuckDuckGo*. One reason *Ghostery* is slower than *uBlock Origin* and *AdblockPlus* here is that to achieve maximum performance while matching as well as minimize memory usage, there is a bit more work to do up-front. In practice, this does not matter so much since it is a one-time operation and subsequent loads are performed from cache. This is really fast. In fact, we can even perform the parsing backend-side and just ship the serialized version of the blocker, which removes this step completely. Time to parse Easylist without Brave ### 8. Conclusion In this study, we looked closely at the performance of some of the most popular content-blockers in use today. In particular, we focused on the efficiency of their network filtering engines, which is the most CPU intensive task they perform. This work was motivated by one of the claims formulated in the [Manifest V3 proposal](https://bugs.chromium.org/p/chromium/issues/detail?id=896897) of the Chromium project: *"the extension then performs arbitrary (and potentially very slow) JavaScript"*, talking about content-blockers' ability to process all network requests. ### Key findings: * We do not think the Manifest V3 claim is accurate as all popular content-blockers are already very efficient and should not incur any noticeable slow-down for users. * Moreover, the efficiency of content-blockers is *continuously improving*, either thanks to more innovative approaches or using technologies like WebAssembly to reach native performance. * While most content-blockers are indeed efficient, they are not equivalent. We observed that *Ghostery* performs consistently as well or better across all dimensions, often surpassing other libraries. We hope these benchmarks will give content-blocker developers the opportunity to measure their own progress against other popular libraries. This will benefit all users, no matter which extension they use, as the efficiency of content-blockers improves. [*Edit of 20-02-2019*](https://github.com/ghostery/whotracks.me/pull/154): The study has been updated with the specific version of each content-blocker measured. [*Edit of 15-03-2019*](https://github.com/ghostery/whotracks.me/pull/161): DuckDuckGo's description has been amended to more accurately describe the way their content-blocker is used in practice: focusing on blocking third-party trackers, but not ads. ================================================ FILE: blog/block-third-party-cookies.md ================================================ title: Third-party cookies - the guests who won't leave subtitle: How the web ecosystem is preventing us from reverting the third-party cookie mistake. author: privacy team type: primer publish: True date: 2018-08-27 tags: blog, cookies header_img: blog/blog-third-party-cookies.jpg +++ Summary: _In this post we describe_ * _How third-party cookies are the cause of multiple privacy and security issues on the web._ * _That, despite the cookie spec writers recommending against it, all major browsers ended up allowing third-party cookies by default._ * _How several major sites and services, including those from Microsoft and Google, fail badly when third-party cookies are blocked._ * _How Cliqz and Ghostery are defusing the privacy issues of third-party cookies by blocking them, while preventing the breakage issues caused by developers assuming a cookie free-for-all._ --- Cookies are a fundamental browser technology which enables state to be kept between browser and servers over the normally stateless HTTP protocol. Cookies enable sites to remember your login, what you've put in your shopping cart, and allow a site to remember language or currency preferences. These features enable the sites you're visiting, 'first parties', to improve your experience and provide some content behind authentication, only accessible to you. However, browsers also, by default, send cookies to any third-parties embedded by the site operators. In some cases these can be used to allow third-party widgets, such as [Disqus](../trackers/disqus.html) comments, to automatically log you in to embedded content in the page. However, it also enables these third-parties to track your browsing across the web. Allowing cookies to third-parties opens up a privacy hole in your browser. On [many sites](../websites.html), just visiting a page will set cookies for over 50 different third-party domains. Each of these are setting cookies so they can [correlate requests](./how_facebook_knows_exactly_what_turns_you_on.html) coming from your browser over days, months, or even years. For example, when you visit any page with a Facebook widget (or visit Facebook itself), they will set a cookie which will only expire in 2 years time. Some google.com cookies expire in 20 years. The `facebook.com` and `google.com` domains are present as a third-party to [24%](../trackers/facebook.html) and [30%](../google.html) of page loads on the web respectively, allowing these services to tracker this proportion of the average user's web browsing history.
Google's consent cookie
A user identifier cookie on the Economist which expires in 2086

Google's consent cookie lasts for 20 years; A tracking cookie on the Economist which lasts for 68 years.

Third-party cookies also represent a security risk to you. [Cross-site request forgery](https://en.wikipedia.org/wiki/Cross-site_request_forgery) (CSRF) attacks are based on the idea that I can make a third-party request to a site that the browser has previously authenticated with, and the browser will send the credentials with the request. If browsers did not allow third-party cookies these attacks would be much harder to exploit than they currently are. These kinds of attacks have been around for over 15 years, and methods to mitigate them are [still being proposed](https://blog.mozilla.org/security/2018/04/24/same-site-cookies-in-firefox-60/), while browser-side protection, such as [first-party isolation](https://wiki.mozilla.org/Security/FirstPartyIsolation), have very limited distribution. Furthermore, the use-cases which legitimately use third-party cookies, like Single-Sign-On portals, or third-party authentication mechanisms, have alternatives which do not require cookies. Sites using a centralised authentication domain can obtain authenication tokens via first-party redirects, and OAuth[^2] can be used to log in to sites using third-party credentials. These mechanisms have the added bonus of transparency and implied consent: When a user logs in with Facebook on a site, the user is actively allowing this connection between the site and Facebook to proceed. So why do we have third-party cookies? Actually, the original 1997 [RFC Specification](https://tools.ietf.org/html/rfc2109) of the cookie standard proposed that third-party cookies should not be allowed on privacy grounds: > This restriction prevents a malicious service author from using unverifiable transactions to induce a user agent to start or continue a session with a server in a different domain. The starting or continuation of such sessions could be contrary to the privacy expectations of the user, and could also be a security problem. and browsers should have this setting by default: > User agents may offer configurable options that allow the user agent, or any autonomous programs that the user agent executes, to ignore the above rule, so long as these override options default to "off". However, these recommendations were not implemented by browser developers at that time, and the default of _'allow all cookies'_ has remained since then. Currently, almost all major browsers have a default to allow all cookies. The one exception is Safari, which only allows third-party cookies for domains which have been visited as a first party. This setting mitigates tracking from unknown domains, but still allows others to track, and does not prevent CSRF attacks. Mozilla also previously [attempted](https://blog.mozilla.org/netpolicy/2013/02/25/firefox-getting-smarter-about-third-party-cookies/) to change Firefox's default handling of third-party cookies in 2013, but pressure from the Ad industry led to a [U-turn](https://blog.mozilla.org/blog/2013/05/10/personalization-with-respect/) before these changes went live. The failure of browsers to handle third-party cookie tracking [has argueably led](https://medium.com/the-graph/how-to-reverse-publisher-revenue-drain-c33e41bf0665) to the increase in adblocker usage since then. The effect that this default has had over the last 20 years, is that developers now assume that cookies are allowed in all contexts. This causes many workflows to break once this assumption is broken. This leads to a vicious cycle, where attempts to limit third-party cookies are foiled because they break too many sites. Apple's push to reduce third-party cookie tracking with their [Intelligent Tracking Prevention](https://webkit.org/blog/7675/intelligent-tracking-prevention/) technology had to include a section to explain to developers how to solve several use-cases when their cookies are limited. This technology still allows third-party cookies from visited sites however, and this method is also recommended for implementing single sign-ons. ## Moving away from third-party cookies In 2015 Cliqz[^1] released an anti-tracking technology which [aggressively blocks third-party cookies](./how_cliqz_antitracking_protects_users.html). Third-party cookies are blocked unless certain heuristics are triggered. These heuristics aim to mitigate common cases where cookie blocking breaks workflows, but also require user action to trigger. A Facebook button can be loaded without cookies, but if the user then clicks on it, there is an implied consent to allow the cookies in this case. This method blocks 97% of third-party cookies, with minimal breakage of pages.
Browser Default Cookie setting
Google Chrome Allow all.
Mozilla Firefox Allow all.
Apple Safari Allow from visited; tracking cookies limited.
Cliqz Browser / Ghostery extension Block all third-party, unless user interaction or compatibility exception.
In December 2017, this technology was included in the [Ghostery 8 release](https://www.ghostery.com/blog/product-releases/browse-smarter-with-ghostery-8/). This increased the number of users with this aggressive cookie blocking behaviour and this increased exposure also highlighted more cases where cookie blocking causes problems for websites. In many cases it may not be surprising that developers have not considered or tested the possibility of third-party cookies not being allowed. What surprised us though, is that this is so pervasive that the biggest players fail to handle cookies properly, in some cases causing critical bugs. At present, if you browse the web with third-party cookies disabled, you may come across issues logging in and making payments. Here we outline several cases we have found involving major tech companies (which should be able to solve this issues), which have consequences from just preventing login or completing payment, to potentially leaking private company data from a Microsoft Office account. ### Microsoft Logout Issues We found that, when all third-party cookies are disabled, logging out on office.com seems to succeed, but actually fails. Additionally this authenication remains available when subequently navigating to office.com, which can be used to extract data from the SharePoint API. This issue was submitted to Microsoft but was rejected on the grounds that it is not remotely exploitable. It is however a risk on any shared computer that a subsequent user would be able to see document metadata for the organisation, and perhaps modify data via the SharePoint API. This can be simply reproduced by disabling third-party cookies in any browser, then logging in and then out again on office.com. Office logged out message

Looks like I'm logged out...

After the confirmation of a successful logout, simply navigate back to www.office.com, and one is returned to the view after login, including an up-to-date feed of recently changed documents. Office documents shown after logout

Document change feed still shown after logout.

The API that makes this information available after logout is fetched via the [SharePoint REST API](https://docs.microsoft.com/en-us/sharepoint/dev/sp-add-ins/sharepoint-net-server-csom-jsom-and-rest-api-index), and the authentication token for this is not deleted nor expired after the failed logout - hence the page can continue to access this information. The token can be collected from the developer tools and then reused for API calls, for example to list folders in this organisation's SharePoint: ```javascript var accessToken = "eyJ0..."; var baseUrl = 'https://org-my.sharepoint.com/_api/'; var headers = new Headers(); headers.append('Authorization', `Bearer ${accessToken}`); headers.append('Accept', 'application/json;odata=verbose'); fetch(`${baseUrl}web/lists`, { headers }) .then(resp => resp.json()) .then(res => console.log(res)) ``` The broken logout state can only be resolved by manually deleting office.com cookies. We also found the session may eventually be expired, but this only happened after multiple hours. Hence, users affected by this will 1) likely not be aware that they're not logged out properly, as the logout appears to be successful, and 2) would not be able to logout anyway if they noticed the issue. The issues with Office continue when trying to purchase an Office365 trial from `https://products.office.com/try`. This time, the source of the problem is detected, but the user is given no choice to continue unless they compromise their security and privacy by enabling cookies. Ironically, they also imply that allowing third-party cookies is somehow safer. Cookie error in Microsoft office checkout

It is not possible to buy Office without allowing third-party cookies.

### Pay with your Cookies It is common practice for E-Commerce sites to embed payment systems from third-party vendors, such as Paypal, on their checkout pages. Such widgets should not require third-party cookies - usually the user can be redirected to pay at the payment provider's site. This method is preferable, as it reduces the chances of phishing: loading the payment page as a first party will make the url and certificate status visible, and only prompting users to enter payment information on the first party site is also good practice. Despite this, we see examples of payment being blocked when third-party cookies are disabled. One such example is on the German E-Commerce site [Thomann.de](../websites/thomann.de.html). When attempting to checkout with Amazon pay, we get an error mentioning that third-party cookies are being blocked: Amazon Pay error on Thomann.de

"There was an error processing the Amazon payment. A possible cause is third-party cookie blocking."

### Connect with Google? Third-party cookies required Many sites use Google's connect SDK, to allow users to login to sites with their Google account. When testing cases on [www.tripadvisor.com](https://www.tripadvisor.com) and [www.stumbleupon.com](https://www.stumbleupon.com) with third-party cookies disabled, the 'Connect with Google' button fails to do anything when clicked. Both these sites also offer Facebook login too which works with cookies disabled. It is not clear why the Google implementation requires third-party cookies to be allowed. Tripadvisor connect social

Tripadvisor signup buttons.

### Please let me track your tracking opt-out Following GDPR, websites using third-party services which collect data about users [acquire consent](./update_jun_2018.html) for this, as well as provide a reasonable way of opting-out of data collection and processing. While many publishers have converged on a solution which [gathers consent as a first-party cookie](https://iabtechlab.com/standards/gdpr-transparency-and-consent-framework/) which can then be passed to third-parties, other still rely on an older system of setting opt-out cookies for each vendor. Obviously, if third-party cookies are blocked, this mechanism will not work, as can be seen on the [Telegraph](../websites/telegraph.co.uk.html): Cookie opt out on telegraph.co.uk

"You browser is currently blocking 3rd party cookies ... you will need to enabled 3rd party cookies if you want all of the opt-outs on this page to work."

In this case, users with third-party cookies disabled will be denied their right to opt-out (though blocking these cookies will effectively prevent a large proportion of tracking). Third-party vendors may say that this mechanism is required in order to remember a user's consent settings. However, previous attempts to allow browsers to convey tracking consent explicitly to servers, via the ['Do Not Track'](https://www.w3.org/TR/tracking-dnt/) standard were killed by the same vendors collectively saying they would [ignore this signal](https://blogs.harvard.edu/doc/2015/09/23/how-adtech-not-ad-blocking-breaks-the-social-contract/). ## Conclusion When the idea of cookies was first proposed, the standard writers were concerned about the privacy implications of allow third-party cookies, and specified that browser vendors should disable them by default. Fast-forward 20 years and the majority of browsers on the web will allow all third-party cookies. The result of this are significant challenges to protect against Cross-site request forgery, with countless sites and accounts compromised along the way, and pervasive privacy invasion in the form of cross-site tracking of users. We argue that we should aim to return to a web where third-party cookies are blocked by default, and are making that possible for users of our anti-tracking technology in [Cliqz](https://cliqz.com/) and [Ghostery](https://www.ghostery.com/), however this is made difficult by the prevailing assumption that cookies are a free-for-all, making many sites fail to function properly in this environment. In this regard we are constantly improving heuristics to mitigate the breakage issues we do find. We showed multiple cases where the assumption that third-party cookies will be allowed lead to both benign and potentially dangerous issues for users who block cookies. Some of these cases affect payments, so perhaps if cookie-blocking becomes more common and companies' bottom lines are effected these issues will be fixed. This is a chicken and egg problem though, if the web is broken for users blocking cookies, then we may never achieve the critical mass required to get it fixed. **For users**, getting control over which cookies your browser sends out, and to whom, is a key part of protecting privacy online, but also something that is not universally recognised by browser privacy tools. Most adblockers, for example, do nothing to the cookies of third-party requests which are not on their blocklists. More adoption of the kind of cookie blocking that Cliqz and Ghostery do help us to achive this critical mass, and push more websites to ensure that their services still work correctly for users who chose more private browser configurations. **Developers** have a part to play here too. By building services which do not require third-party cookies, or at least continue to function without them, it becomes easier for users to turn off third-party cookies, and the web becomes more privacy-friendly. As we have seen in this article, even the biggest tech companies are currently failing at this, but this seems to be more due to a lack of awareness, than any difficultly in implementation. [^1]: Disclosure: WhoTracks.Me is operated by Cliqz. [^2]: Note that both of these methods also have some privacy issues. First-party redirection has been exploited for [user tracking](https://brave.com/redirection-based-tracking/), and OAuth dialogs can trick users into granting [many more permissions](https://lifehacker.com/how-to-revoke-pokemon-go-s-extensive-permissions-to-you-1783466118) than they actually need. ================================================ FILE: blog/cookie-consent.md ================================================ title: Improving Cookie Consent subtitle: Cliqz' new feature to make consent fairer author: privacy team type: article publish: True date: 2019-11-28 tags: blog, gdpr, consent header_img: blog/autoconsent/cookie-blocker-prompt.png +++ Since the GDPR came into force in May last year, the Cookie-Consent Popup has become a fixture of browsing the web. These popups are ostensibly there to allow you to choose whether you agree or disagree to your data being used for certain purposes on the site, but confusing UI design and tricks mean that many users are not able to select their desired consent settings. A recent [study](https://arxiv.org/pdf/1909.02638.pdf) showed that user fatigue with consent popups, and simple UI tricks are able to artificially inflate the opt-in rate. The study also showed that, when opt-out is the default, only 0.1% of users would consent to all data processing. This is in stark contrast to the over 90% opt-in rate that the [industry claims](https://www.thedrum.com/news/2018/07/31/over-90-users-consent-gdpr-requests-says-quantcast-after-enabling-1bn-them), and uses to justify that users are OK with tracking. How can we restore balance to this situation, and allow users a fair choice about how their data is used? At Cliqz we have been developing a new feature to aim to address the difficulty of denying consent based around 3 core principles: 1. Opt-out and opt-in should both require maximum of one click, i.e. the time-cost should be the same, no matter which choice is made. 2. The user should not have to decide individually for every site. Their default choice can be used to give consent after their initial decision. 3. Consent banners only offering an 'OK' or 'Allow' option do not allow user choice. The are at best a distraction for the user, and at worst drive consent fatigue and encourage the bad practice of automatically clicking away message prompts. These should be hidden. Unfortunately, implementing an automated consent choice in the browser is made challenging by the lack of adoption or adherence to browser standards. The [Do Not Track](https://www.w3.org/blog/2018/06/do-not-track-and-the-gdpr/) standard enables users to broadcast preferences around tracking, and for sites to communicate tracking status to the browser. Before that, the [P3P Project](https://www.w3.org/P3P/) attempted to standardise privacy practices and allow automated decision making around them. Both of these standards have been rejected by the tracking industry, who prefer to present consent on their terms. The industry have instead proposed and implemented the [Transparency and Consent Framework](https://iabeurope.eu/transparency-consent-framework/), which primarily focuses on communicating consent between vendors. It is a read-only API, so the browser can only read the consent status as set by the site, and not modify it. This means that consent can currently only be expressed by clicking through HTML forms. Navigating a Cookie-Consent Popup manually

Navigating a Cookie-Consent Popup manually.

Luckily, the number of vendors offering consent solutions is limited, and browser extensions can simulate clicking through forms. Thus, [autoconsent](https://github.com/cliqz-oss/autoconsent) was born - a library of rules standardising the navigation of consent forms for the most popular sites and vendors. This library is able to: * Detect the presence of supported Consent Management Providers on a page. * Determine whether a popup or overlay is being shown on the page. * Execute an opt-in (allow all purposes) or opt-out (reject all purposes). * Where available, re-open the popup to allow modification of the settings. In practice, this allows consent popups to be rapidly dismissed when loading a new site. The speed depends on the provider and how quickly their UI can be manipulated. In all cases, however, this is faster than a user could navigate the interface. Automatic navigation of the Cookie-Consent Popup

Automatic navigation of the Cookie-Consent Popup.

For popups that are informational only, or force affirmative consent, we apply simple cosmetic rules. These are CSS rules that define elements in the page that should be hidden. As with the consent rules, we benefit from the defacto standardisation of tools for displaying of popups, such that a small number of rules can support the majority of popups shown by websites. These elements combined mean that we now just have to ask the user once whether they want to opt-in or opt-out, then they will not be bothered by consent popups on the majority of sites they visit. At the same time, they will signal to these sites their approval or dissapproval of their data collection practices. This signal of non-consent is important to encourage and incentivise a shift in data usage practices on the web. When sites realise they cannot just trick users into allowing invasive data collection, they will have a strong incentive to change the way they operate and respect users more. The new Cliqz Cookie-Popup blocker is available in the latest version of the Cliqz browser. Get it at [cliqz.com](https://cliqz.com/download). ================================================ FILE: blog/cookies.md ================================================ title: Cookies subtitle: A small piece of data sent from a website, meant to 'help', used to track. author: privacy team type: primer publish: True date: 2017-07-22 tags: primer, tracking header_img: blog/blog-cookies.jpg +++ An HTTP cookie (also called web cookie, Internet cookie, browser cookie, or simply cookie) is a small piece of data sent from a website and stored on the user's computer by the user's web browser while the user is browsing. Cookies were designed to be a reliable mechanism for websites to remember stateful information (such as items added in the shopping cart in an online store) or to record the user's browsing activity (including clicking particular buttons, logging in, or recording which pages were visited in the past). They can also be used to remember arbitrary pieces of information that the user previously entered into form fields such as names, addresses, passwords, and credit card numbers. Other kinds of cookies perform essential functions in the modern web. Perhaps most importantly, authentication cookies are the most common method used by web servers to know whether the user is logged in or not, and which account they are logged in with. Without such a mechanism, the site would not know whether to send a page containing sensitive information, or require the user to authenticate themselves by logging in. The security of an authentication cookie generally depends on the security of the issuing website and the user's web browser, and on whether the cookie data is encrypted. Security vulnerabilities may allow a cookie's data to be read by a hacker, used to gain access to user data, or used to gain access (with the user's credentials) to the website to which the cookie belongs (see cross-site scripting and cross-site request forgery for examples).[[1](http://news.cnet.com/8301-10789_3-9918582-57.html)] ## Tracking Cookies The tracking cookies, and especially third-party tracking cookies, are commonly used as ways to compile long-term records of individuals' browsing histories – a potential privacy concern that prompted European [[2](http://webcookies.org/faq/#Directive)] and U.S. lawmakers to take action in 2011. European law [[3](http://www.bbc.co.uk/news/technology-12668552)] requires that all websites targeting European Union member states gain "informed consent" from users before storing non-essential cookies on their device. The excerpt above has been retrieved from [wikipedia](https://en.wikipedia.org/wiki/HTTP_cookie). #### References: [1] [Gmail cookie stolen via Google Spreadsheets](http://news.cnet.com/8301-10789_3-9918582-57.html)
[2] [What about the "EU Cookie Directive"?](http://webcookies.org/faq/#Directive)
[3] [New net rules set to make cookies crumble](http://www.bbc.co.uk/news/technology-12668552)
[4] Source: [Wikipedia](https://en.wikipedia.org/wiki/HTTP_cookie) ================================================ FILE: blog/dexie_transaction_bug.md ================================================ title: A quantum bug in Firefox Quantum subtitle: DevTools - how we tracked down an observant-dependent bug. author: privacy team type: article publish: True date: 2019-04-30 tags: blog header_img: blog/dexie_transaction_bug/release.png +++ Summary: _When observing your own program can change its behavior, and an unexpected but real use-case to detect if DevTools are open._ --- Occasionally one comes along weird bugs, some might even call them mystic or heisenbugs. We recently stumbled upon one such bug while working on the [Cliqz Extension](https://cliqz.com/en/). It started with a rather innocent warning in our browser console. While testing an upcoming release, we noticed some warnings emitted from the [Dexie.js](https://dexie.org/) library, which is a popular wrapper for IndexedDB, a browser database API: Unhandled rejection: r@moz-extension://a4671cfd-e7e2-4264-9f96-b21f9b289cd9/modules/vendor/dexie.min.js:1:25586 _promise/i<@moz-extension://a4671cfd-e7e2-4264-9f96-b21f9b289cd9/modules/vendor/dexie.min.js:2:1746 U@moz-extension://a4671cfd-e7e2-4264-9f96-b21f9b289cd9/modules/vendor/dexie.min.js:1:6115 q@moz-extension://a4671cfd-e7e2-4264-9f96-b21f9b289cd9/modules/vendor/dexie.min.js:1:5934 _promise@moz-extension://a4671cfd-e7e2-4264-9f96-b21f9b289cd9/modules/vendor/dexie.min.js:2:1720 _trans@moz-extension://a4671cfd-e7e2-4264-9f96-b21f9b289cd9/modules/vendor/dexie.min.js:1:25312 _idbstore@moz-extension://a4671cfd-e7e2-4264-9f96-b21f9b289cd9/modules/vendor/dexie.min.js:1:25632 get@moz-extension://a4671cfd-e7e2-4264-9f96-b21f9b289cd9/modules/vendor/dexie.min.js:1:25747 getTag/<@moz-extension://a4671cfd-e7e2-4264-9f96-b21f9b289cd9/modules/webextension-specific/app.bundle.js:15679:35 … At first, it did not look too serious, but after digging in a bit more, it turned out that core functionalities of both the Cliqz and [Ghostery extensions](https://www.ghostery.com/) were negatively impacted. Each time the extension attempted to aggregate some statistics for display on our FreshTab page, the operation would mysteriously fail. And this is how the investigation started... We quickly noticed that the bug could neither be reproduced on the [Cliqz Browser](https://cliqz.com/en/) nor on Firefox 66 (which is at this time the stable release). Only Firefox 67 (developer edition) and Firefox 68 (Nightly) were affected. Further testing confirmed that the issue could be reproduced with previous versions of the Cliqz and Ghostery extensions (which share part of the code base). At this point we suspected a bug in Dexie or IndexedDB and started looking deeper. We realized that whenever a Dexie operation was attempted as part of a transaction, the operation would fail, a warning would be displayed, and the transaction would abort. With that insight, we were able to create a first workaround by avoiding the use of transactions. Still, it was not clear why the code would fail on Firefox 67 and simply avoiding transactions without understanding the root cause was not satisfactory. For this reason, we tried to narrow it down further. A first attempt to reproduce the problem with a minimal extension and the same type of transactions was not successful. All database operations succeeded normally; the issue seemed to happen only as part of the Cliqz extension. Then we started trimming down our bundle by removing code from the Cliqz extension, step by step. We had the feeling that it had to be a kind of timing issue, but it was not clear which conditions were necessary or sufficient to trigger the bug. It took us time to realize, but finally we had the Eureka moment! It only seemed that the transactions would fail when DevTools were opened! This was literally a heisenbug, only triggering when observed in the console. Not opening the console would be enough to make the code work as expected. In other words, if the code ran fast enough that we did not have time to open the debugger, everything would work perfectly. It was a race condition indeed, but not in the traditional sense of the term, rather it was a race between the developer and the code... With that understanding, we were able to create a minimal example and could file proper bug reports for both Firefox and Dexie.js: * [Bug 1545400 - Webextension console dev-tools opened makes IndexedDB/Dexie transaction fail](https://bugzilla.mozilla.org/show_bug.cgi?id=1545400) * [Dexie #831 - Transaction fails if console dev-tools opened (Firefox 67)](https://github.com/dfahlander/Dexie.js/issues/831) Detecting whether DevTools are open === Let us take a closer look at how this bug can be used to run code only when DevTools are opened. Ideally, a browser should not allow that, although it is difficult to prevent it in practice. Some discussion on that topic can be found in [this Chromium issue](https://bugs.chromium.org/p/chromium/issues/detail?id=672625). This has been exploited in the past by some websites to hide malicious behavior from the eyes of developers or users; whenever DevTools would be opened, the site would conceal some of the logic immediately, preventing inspection. Most existing techniques to detect DevTools are leveraging browser bugs, which eventually got patched (examples can be found [here](https://stackoverflow.com/q/7798748/783510) and [here](https://github.com/sindresorhus/devtools-detect/issues/15)). Typically, they use quirks in various Browser APIs, but there are also solutions based on timing attacks. Another approach is to look at the window size, which is currently used in the [devtools-detect library](https://github.com/sindresorhus/devtools-detect). The drawback is that it can be easily bypassed if the DevTools are opened in a separate window. Using the transaction bug, we can build a DevTools detector for Firefox 67 and above that will also work for undocked windows. Here is a sketch of the idea: function onDevToolsOpen() {…} function onDevToolsClosed() {…} const db = new Dexie('test_db'); db.version(1).stores({ test_table: 'test_key' }); setInterval(() => { // uses the bug that the Dexie transaction will fail // with open DevTools in Firefox 67 and above db.transaction('rw', db.test_table, async () => { await db.test_table.toArray() }).then(onDevToolsOpen, onDevToolsClosed); }, 1000); If you want to try yourself, a working example is available [here](https://cdn.cliqz.com/browser-f/fun-demo/firefox_devtools.html). Until a patch for the bug gets released, the technique will work in pages and as well as in web extensions. In the [bug ticket](https://bugzilla.mozilla.org/show_bug.cgi?id=1545400), we also included a working example that detects DevTools from within a WebExtension. This issue joins an array of techniques which enable malicious actors to detect special states of the browser, such as private browsing mode, and when users are [auditing the activities of sites with DevTools](https://github.com/gorhill/uBO-Extra#purpose). The former has been used to [deny access to sites in private browsing mode](https://arstechnica.com/information-technology/2017/05/boston-globe-website-no-longer-lets-you-read-articles-in-private-mode/), attempting to force users to reduce their protection against tracking, while the latter is used by [systems which circumvent adblocker to cover their tracks](https://www.theregister.co.uk/2017/08/11/ad_blocker_bypass_code/). ================================================ FILE: blog/fingerprinting.md ================================================ title: Fingerprinting subtitle: Let me tell you what's unique about your device. author: privacy team type: primer publish: True date: 2017-07-22 tags: primer, tracking header_img: blog/blog-fingerprinting.jpg +++ A device fingerprint or machine fingerprint or browser fingerprint is information collected about a remote computing device for the purpose of identification. Fingerprints can be used to fully or partially identify individual users or devices even when [cookies](/blog/cookies.html) are turned off. Basic web browser configuration information has long been collected by web analytics services in an effort to accurately measure real human web traffic and discount various forms of click fraud. With the assistance of client-side scripting languages, collection of much more esoteric parameters is possible [[1](http://browserspy.dk/)]. Assimilation of such information into a single string comprises a device fingerprint. In 2010, EFF measured at least 18.1 bits of entropy possible from browser fingerprinting, [[2](https://panopticlick.eff.org/static/browser-uniqueness.pdf)] but that was before the advancements of canvas fingerprinting, which claims to add another 5.7 bits. Prior to early 2017, device fingerprinting was limited to single browsers. If a user switched browsers regularly, fingerprinting could not be used to link the user to these browsers [_citation needed_]. A cross browser fingerprinting method has been published [[3](http://yinzhicao.org/TrackingFree/crossbrowsertracking_NDSS17.pdf)] which allows tracking of a user across multiple browsers on the same device. The excerpt above has been retrieved from [wikipedia](https://en.wikipedia.org/wiki/Device_fingerprint). ## References [1] [Browser Spy](http://browserspy.dk/)
[2] EFF: [How Unique Is Your Web Browser?](https://panopticlick.eff.org/static/browser-uniqueness.pdf)
[3] [(Cross-)Browser Fingerprinting via OS and Hardware Level Features](http://yinzhicao.org/TrackingFree/crossbrowsertracking_NDSS17.pdf)
[4] Source: [Wikipedia](https://en.wikipedia.org/wiki/Device_fingerprint) ================================================ FILE: blog/gdpr-what-happened.md ================================================ title: GDPR - What happened? subtitle: The tracking landscape post GDPR, adverse effects on competition and a market for compliance technologies author: privacy team type: article publish: True date: 2018-09-03 tags: blog, gdpr header_img: blog/gdpr/gdpr-header.png +++ _In this article we look at the effect GDPR has had on the tracking landscape, online advertising in Europe, and provide a set of recommendations for machine readable legislation._ ## GDPR: A primer Having been a hot topic of discussion for at least the last 2 months, it is unlikely that GDPR needs an introduction. So in brief: the regulation applies to the processing of personal data of European citizens. Companies engaged in such processing activities are subject to compliance, regardless of whether or not they operate in the EU[^1]. An important aspect of the regulation is the pressure put on companies to obtain consent from their European users for the processing of their personal data. This is why prior to May 25th, 2018 there was a surge in emails in your inbox, and it's also the reason for the numerous popups you see when visiting websites. GDPR was announced two years before it came into force, and it was not the overhaul of privacy legislation one could believe it was from reading the press. In fact, GDPR came as a major update of the previous EU Data Protection Directive[^2] which has been around since 1995. Designed to harmonize the data protection legislation in the EU, and catch up with technological progress in the last 23 years, GDPR comes heavily loaded with legal language that many have found hard to navigate. Daphne Keller of the Standford Center for Internet and Society said "*The final GDPR text is riddled with ambiguous passages*", suggesting that the ones who will benefit the most from GDPR are data protection lawyers[^3]. **So, what happened?** This is the question we are all left with. We will be using whotracks.me data to make sense of the effect of GDPR on the tracking landscape in the web and on online advertising, the behemoth of third party services on the web. ## Tracking Landscape on Websites We take 2000 [websites](../websites.html) profiled on **whotracks.me** and compare the tracking landscape in these sites as a function of the origin of the users visiting. We want to compare the EU, subject of GDPR, with the US. ![mean_third_parties](../static/img/blog/gdpr/f1-eu-us-comparison.svg) Merely looking at the average number of trackers per page for each category of site being visited reveals a general downward trend in Europe. The opposite is true in the US. The blue area indicates the average number of trackers across categories. In fact, if we look closely, since April 2018 the average number of trackers per page in the EU has dropped by almost 4% while in the US it has increased by 8%. ![average number of trackers EU-US](../static/img/blog/gdpr/mean_eu_us.svg) If we take the top 2000 domains visited by European residents, and check how the average number of trackers per page by the category of the website, we notice that there as been a reduction in the number of third parties almost across the board. ![category change eu](../static/img/blog/gdpr/category_change_eu.svg) The reduction seems more prevalent among categories of sites with a lot of trackers. We see a 7.5% reduction in the average number of third parties per page from April to July in News websites. This is in the same direction as what was identified by a study published last month by the University of Oxford. They looked at news websites, and found that the number of cookies set on page decreased on average by 22%[^4]. Some websites, like [The Los Angeles Times](../websites/latimes.com.html), interrupted their operations in Europe, others decided to offer text only versions of their websites if the user does not consent to sharing data with third parties, like [npr.org](../websites/npr.org.html). What we are certainly observing is a rise in the usage of consent management tools, for which we [wrote about in more detail](./update_jun_2018.html) back in June. ## Third party services: the winner takes it all Both the most lucrative, and the most pervasive of all services performed by third parties is online advertising - the 'fuel' that keeps a large part of the web running. Online advertising in 2018 is estimated to be a $270 billion[^5] market, and expected to grow by more than 20% in the next two years. This is the market that third parties are competing for, and when the prize is so high, worries over GDPR having regressive effects on competition are understandable. As in most markets, the presence of monopolies is something regulators try hard to avoid[^6]. The question is then: ***Has GDPR, designed to enhance user privacy in the web, had any adverse effects on competition?*** At present, whotracks.me has profiles for more than 1000 trackers, out of which about **200** are classified as advertising services. For each of these trackers we have data on what percentage of the measured web traffic we have observed the tracker to be loaded - `reach`[^12], as well as what percentage of websites the tracker is present on - `site_reach`[^13]. Monitoring `reach` and `site_reach` gives us interesting insights into the structure of the market these trackers operate in, as well as their relative market share. Using whotracks.me data, we can do that at scale. Each month we have on average about 300 million page loads and more than half a million websites. If we rank each tracker by its reach, and measure changes in reach since April, we notice that in Europe, most advertisers appear less. ![advertising market share](../static/img/blog/gdpr/advertisers_market_share_reach.svg) The same trend persists when we look at `site_reach`. ![advertising market share](../static/img/blog/gdpr/advertisers_market_share_site_reach.svg) Google's advertising services have maintained their market share, while other advertisers across the board have lost reach. There could be several reasons to explain Google's favorable state post GDPR: 1. **Resources thrown at compliance**: Google and other big companies have had significant resources dedicated to compliance[^7]. 2. Google acts in the capacity of a gatekeeper, hence it is conceivable to assume it may have used that position in punitive ways. Reports indicate that Google could have encouraged publishers to reduce the number of AdTech vendors[^8]. 3. Websites owners trying to minimize their exposure opt for 'safer choices', dropping smaller advertisers that may have a harder time proving compliance. Using a tracker's reach as a proxy for market share, we measure that GDPR may have had regressive effects on competition in the online advertising space in Europe. ## Recommendations for GDPR 2.0 GDPR has had a measurable impact on advancing the rights of European citizens on the web. We believe one of the most important contributions of GDPR is the increased transparency on how personal data is moved around, as well as the management of consent from the user, and think GDPR 2.0 should strive to be **machine-readable** as opposed to **human-readable**. GDPR came as an update to the EU Data Protection Directive, primarily because the evolution of the web in the last 20 years rendered the old regulation obsolete. **Human Readable** is not the way to design a law that aims in large part to regulate interaction in the web. The design of the tools meant to empower users are left to the service providers, whose incentives don't exactly align with that of the users. This can give rise to deceptive interfaces and UX patters, designed to exploit human cognitive biases. What we need, is a GDPR 2.0 that pushes for *machine readable* standards, giving rise to user-focused solutions, simple, non-deceiving interfaces, thus creating an industry of privacy and compliance, where technologists keep other technologists in check. Here are our recommendations: 1. **`/privacy-policy.txt`** - require websites to host the privacy policy in a standard location of the sitemap. At present, identifying the location of the privacy policy of a website is not as straightforward as one may hope. Last year as part of the Mozilla Global Sprint we built [Privacy Bot](https://github.com/cliqz-oss/privacy-bot), which aimed to gather, persist and analyze privacy policies. One of the challenging problems we had to solve, was identifying where the privacy policy was hosted. 2. **`/third-parties.json`** - provide a structured list of third parties present on the site, the service being performed by them, list of data points they have access to (e.g. IP, user agent, pages visited on site ... ), and default state of consent. This would enable browsers to assume the role they should have had baked in: a unified control center for the user to manage consent. This is especially important given the rise of deceiving UX patterns we are increasingly used to seeing in websites these days[^9].There are standards that can be built upon, like the Content Security Policy, and the Do Not Track Standards, which are widely adopted by browser vendors. A similar effort is `/ads.txt`, initiated by the iab techlab, aiming towards a mechanism to define authorized sellers for web content from the perspective of the domain owner[^10]. 3. **`/dpo.json`** - increase oversight of the Data Protection Officer, detaching the role further away from the organization. Provide machine parsable details of the DPO, for users to be able to reach out more easily, as well as providing incentives for the establishment of a new market around privacy management. 4. **`/incidents-and-cases.json`** - Data Incidents reported have increased as a consequence of GDPR[^11]. This information should also be made available to the public. The web is currently built and operated largely on trust. As such, transparency over the amount of incidents a given website or service has had to report is very important. Furthermore, provide a list of the open court cases involving the mismanagement of personal data the company is involved in. ## Conclusions In Europe, GDPR has thus far had a measurable impact in reducing the average number of trackers websites put in their pages, while in the US the opposite is true. The increase in transparency benefits users as they enjoy an increased control over their data, but the UX of the services managing that consent does not always have users' best interest in mind. On the other hand, in Europe GDPR has led the online advertising market to become more concentrated, as the majority of advertisers lose market share. If this trend persists, it is possible that GDPR is having adverse effects on competition. For users this means that while the number of third parties asking for access to their data is decreasing, a tiny few are getting more of their data. To Regulators, and especially the supervising authorities responsible for the enactment of GDPR, we think you should strive for creating incentives for industry players to keep each other in check, thereby creating a market for privacy. The only way this can be achieved is by pushing for a machine readable legislation that enforces standards. #### Footnotes [^1]: GDPR Article 3 on [Territorial Scope](https://gdpr-info.eu/art-3-gdpr/) [^2]: EU Data Protection Directive [[source]](https://en.wikipedia.org/wiki/Data_Protection_Directive) [^3]: GDPR is vague [Standford Center for Internet and Society](http://cyberlaw.stanford.edu/blog/2015/12/final-draft-europes-right-be-forgotten-law) [^4]: News Websites post GDPR [[Factsheet]](https://reutersinstitute.politics.ox.ac.uk/our-research/changes-third-party-content-european-news-websites-after-gdpr) [^5]: Digital Advertising Market [[Statista]](https://www.statista.com/statistics/237974/online-advertising-spending-worldwide/) [^6]: Google's Anti-trust cases in Europe [[source]](https://www.reuters.com/article/us-eu-google-antitrust-timeline/googles-antitrust-cases-in-europe-idUSKBN1K81CC) [^7]: Preparation for GDPR [[source]](https://www.theguardian.com/technology/2018/may/25/facebook-google-gdpr-complaints-eu-consumer-rights) [^8]: Google's Funding Choices [[source]](https://adexchanger.com/online-advertising/googles-gdpr-consent-tool-will-limit-publishers-to-12-ad-tech-vendors/) [^9]: Dark Patterns: How UX design tricks you into giving away your privacy [[source]](https://cliqz.com/en/magazine/dark-patterns-how-ux-design-tricks-you-into-giving-away-your-privacy) [^10]: IAB Techlab `ads.txt` [[source]](https://iabtechlab.com/wp-content/uploads/2017/09/IABOpenRTB_Ads.txt_Public_Spec_V1-0-1.pdf) [^11]: Reported Data Incidents increase with GDPR [[source]](https://www.itgovernance.co.uk/blog/ico-statistics-show-increase-in-reported-incidents-ahead-of-gdpr/) [^12]:`reach`: Proportional presence across all page loads (i.e. if a tracker is present on 50 out of 1000 page loads, the reach would be 0.05). Value is a float between 0 and 1. [^13]:`site_reach`: Presence across unique first party sites. e.g. if a tracker is present on 10 sites, and we have 100 different sites in the database, the site reach is 0.1. Value is a float between 0 and 1. ================================================ FILE: blog/generating_adblocker_filters.md ================================================ title: Generating Ad-Blocker filters from whotracks.me data subtitle: Let's never miss a new tracker again. author: remusao type: article publish: True date: 2017-11-20 tags: privacy, tracking, adblocking header_img: blog/blog-generate-adb-filters.jpg redirect_url: https://www.ghostery.com/blog/generating-adblocker-filters +++ *TL;DR* In this post we see how to: 1. Load the data from [whotracks.me](https://github.com/ghostery/whotracks.me) to get access to trackers' information 2. Create a mapping from tracking categories to list of domains 3. Filter each domain based on the amount of tracking of each *app* 3. Generate a filter list for each category The full source code used in this article can be found on the [Github repository](https://github.com/ghostery/whotracks.me/blob/master/contrib/generating_adblocker_filters.py). Most popular content blockers are using filter lists to decide what requests leaving the browser should be blocked. In this regard, filter lists act as a privacy *ground truth*: deciding what is safe, and what is not safe for users. It means that your privacy protection is only as good as the filters your are using. The community is doing an amazing job, but still there can be gaps in your protection; one such situation is when a new tracker appears. With the right data, updated regularly, we believe it is possible to build powerful tools to help increase users' privacy. Knowing more about trackers, in real time, allows to provide better anti-tracking but can also *help* the tedious process of curating the filter lists. In this post we'd like to demonstrate how we can make use of the open-sourced [whotracks.me](https://whotracks.me) data to automatically generate *up-to-date*, *per-category*, filter lists supported by the most popular ad-blockers out there. Leveraging this data can improve user experience and make maintaining the lists easier. In the future, we can imagine generating per-country lists as well, in the spirit of the different [easylists](https://easylist.to/) already in existence: * `DEU: Pornvertising blocking Germany` * `DEU: Site_Analytics blocking Germany` * ... * `FR: Site_Analytics blocking France` They could also be dispatched in the already existing lists such as `advertising`, `privacy`, etc. Another option could be to use this as a tool to assist maintainers to keep an eye on the ecosystem; allowing to learn about new trackers in real time. Let's get started! ## Loading the data The first step is to install the `whotracksme` package, available on [PyPI](https://pypi.python.org/pypi/whotracksme) and [Github](https://github.com/ghostery/whotracks.me). You can get started by installing `whotracksme` with `pip`: ```sh $ pip install whotracksme ``` We start by loading the tracker-related data from [trackerdb.sql](https://github.com/ghostery/whotracks.me/blob/master/whotracksme/data/assets/trackerdb.sql), using the helper function found in the `whotracksme.data` module: ```python from collections import defaultdict from whotracksme.data import load_tracker_db # Categories to tracker domains tracker_domains_per_category = defaultdict(list) # Keep track of normalized "app" name for each tracker domain. A given "app" # such as "doubleclick" can use several domains: 2mdn.net, doubleclick.net, etc. tracker_domains_to_app = {} # Load trackers and group them by category sql_query = """ SELECT categories.name, tracker, domain FROM tracker_domains INNER JOIN trackers ON trackers.id = tracker_domains.tracker INNER JOIN categories ON categories.id = trackers.category_id; """ with load_tracker_db() as connection: for (category, tracker, domain) in connection.execute(sql_query): tracker_domains_per_category[category].append(domain) tracker_domains_to_app[domain] = tracker ``` Here is a sample of what we get in `tracker_domains_per_category`. Note that if you run the same script, you might get slightly different results as the data is being constantly updated: ```python defaultdict(list, { 'advertising': [ 'doubleclick.net', ... ], 'audio_video_player': [ 'soundcloud.com' ... ], 'cdn': [ 'googleapis.com', ... ], 'comments': [ 'disqus.com', ... ], 'customer_interaction': [ 'zendesk.com', ... ], 'essential': [ 'googletagmanager.com', ... ], 'extensions': [ 'kaspersky-labs.com', ... ], 'hosting': [ 'amazonaws.com', ... ], 'misc': [ 'linkedin.com', ... ], 'pornvertising': [ 'pornhub.com', ... ], 'site_analytics': [ 'google-analytics.com', ... ], 'social_media': [ 'twitter.com', ... ] ]}) ``` ## Filtering based on tracking behavior It is tempting to generate filters for each domain loaded so far, but it would be very aggressive. Indeed, some domains identified as potential trackers might in fact not send [unsafe identifiers](https://whotracks.me/blog/what_is_a_tracker.html) (or not a lot). For example [createjs](https://whotracks.me/trackers/createjs.html) is not using any *fingerprinting* and does not seem to be doing tracking via *cookies*, hence, it should not be blocked systematically. Fortunately, we can make use of the data from [apps.json](https://github.com/ghostery/whotracks.me/blob/master/whotracksme/data/assets/apps.json) to learn more about each tracker. An *app* is an entity which can contain several domains (e.g.: *doubleclick* is an *app* for which we identified three domains: `2mdn.net`, `invitemedia.com` and `doubleclick.net`). We also provide information about companies to which each app belongs, but we will leave the exploration of this data for another article. ```python import json from whotracksme.data import load_apps apps = load_apps() ``` `apps` is a dictionary with keys being *app ids* (e.g.: `google_analytics`) and values containing all we know about each *app*. Let's take an example: ```json apps["google_analytics"] { "overview": { "bad_qs": 0.4377430033329568, "content_length": 14771.492718357234, "cookies": 0.0015869678941083753, "https": 0.7507222054912428, "id": "google_analytics", "reach": 0.44292899275150094, "requests": 3.834100333790446, "requests_tracking": 1.202157901660253, "site_reach": 0.616005569531587, "tracked": 0.4383474801843971 }, "history": ..., "rank": ..., "sites": ... } ``` That's a lot of data, and we plan to release a more complete documentation about what all this is about soon. For now let's just say that everything is already made accessible on the website, in form of nice graphs and aggregations! For our use-case, we will only consider the field: `tracked`. It represents the proportion of page loads including *app*, identified as performing some form of tracking (using either identifying *cookies* or *fingerprinting*). In the case of `google_analytics`, it means that out of 100 page loads where `google_analytics` was present, tracking occurred 44 times. Before generating the filter list, let's keep only *apps* tracking users more than `10%` of the time. Please note that finding the right threshold would require some finer analysis, and could depend on the application. ```python def filter_domains(domains): for domain in domains: app_name = tracker_domains_to_app[domain] if app_name in apps: app = apps[tracker_domains_to_app[domain]] tracked = app['overview']['tracked'] if tracked >= 0.1: yield domain ``` We need to check if the *app* exists first because we currently only have the top 500 hosted on Github. We will host more in the future. ## Generating the lists We now proceed to generate the filter lists from these domains. They can take two forms: * ADB compatible syntax: `||{domain}$third-party` * Hostname syntax: `127.0.0.1 {domain}` Note that the second option will probably be too aggressive in a lot of cases, as it will also block the domain even if they are first-party (e.g., `google.com` might get blocked by these rules). ```python def generate_adb_filters(domains): """Given a list of domains, generate filters using the ADB syntax to be used in an adblocker""" for domain in domains: yield f"||{domain}$third-party" def generate_hostname_filters(domains): """Given a list of domains, generate filters using the hostname syntax""" for domain in domains: yield f"127.0.0.1 {domain}" # Generate filters with *ADB* syntax adb_filters = { category: '\n'.join(generate_adb_filters(filter_domains(domains))) for (category, domains) in tracker_domains_per_category.items() } # Generate filters with *hostname* syntax hostname_filters = { category: '\n'.join(generate_hostname_filters(filter_domains(domains))) for (category, domains) in tracker_domains_per_category.items() } ``` Each dictionary now contains a valid adblocking list for each category: ```python hostname_filters.keys() ``` ```python dict_keys([ 'advertising', 'audio_video_player', 'cdn', 'comments', 'customer_interaction', 'essential', 'extensions', 'hosting', 'misc', 'pornvertising', 'site_analytics', 'social_media', 'unknown' ]) ``` And here is what we get for example in the `advertising` category: ```python print(adb_filters['advertising']) ``` ``` ||doubleclick.com$third-party ||criteo.com$third-party ... ``` And the same domains but as `hostname` filters: ```python print(hostname_filters['advertising']) ``` ``` 127.0.0.1 doubleclick.com 127.0.0.1 criteo.com ... ``` To put it in a nutshell, here is what we just did: 1. Load the data from [whotracks.me](https://github.com/ghostery/whotracks.me) to get access to trackers' information 2. Create a mapping from tracking categories to list of domains 3. Filter each domain based on the amount of tracking of each *app* 3. Generate a filter list for each category There is so much more we can do with this database. At the moment the API to load the data is pretty-low level, but it will be improved over time. ================================================ FILE: blog/google_domains.md ================================================ title: The end of google.{your country}? subtitle: Google's move to keep their cookies. author: privacy team type: article publish: True date: 2018-04-23 tags: google, cookies, tracking protection header_img: blog/google_domains/dataflow.png +++ There have been [recent reports](https://twitter.com/vtoubiana/status/987365270187634688) that Google has started redirecting users from regional variants of Google search (served on google.{de, fr, co.uk, etc}) to google.com for search results. This has implications for the [4th most prevalent](../trackers/google.html) tracker on the web, so we decided to check the data to see what is going on. By looking at WhoTracks.Me data from April, we see that around April 16th there is a shift in traffic from Google's European search results pages (`www.google.{de,fr,at,co.uk,etc}`) towards `www.google.com`. The figure below shows that the former domains all saw a 50% drop in number of page loads over the last week, while `www.google.com` is up 100-150%, suggesting Google are doing a gradual rollout of this change. These changes lie well outside the bounds of the weekly traffic fluctuations we usually see. ![The rise of Google.COM](../static/img/blog/google_domains/global_trends.svg)

Percentage change in traffic to google search result pages, April 2018

We can further see the magnitude of this change by focusing on data for Germany. If we look at the relative proportion of pages loaded on `www.google.de` and `www.google.com` in Germany over the last month, we see a marked increase, with the share of traffic to `www.google.com` going up from around 5% to over 40%. ![Relative proportion of traffic in Germany](../static/img/blog/google_domains/germany_prop.svg)

Search results pages used in Germany, April 2018

Why is Google doing this? We don't know - we're not aware of any official announcement. However, one reason for this could be a reaction to increased usage of restrictive cookie settings, such as allowing cookies only from visited sites, or Apple's [Intelligent Tracking Prevention](https://webkit.org/blog/7675/intelligent-tracking-prevention/). If a user is rarely visiting the google.com domain, these technologies can expire this cookie earlier, or prevent its use in third-party contexts. As `google.com` is the domain used to authenticate with Google services, if the browser sends `google.com` cookies in third-party context, these visits can be directly attributed to one's Google profile. Therefore, this change increases the likelihood that the user will have recently visited `www.google.com`, and therefore Google's tracking can continue uninterrupted. Tracking from `google.*` domains [reaches 30% of web traffic](https://whotracks.me/trackers/google.html), and the majority of this reach is contributed by the [`google.com` domain](https://github.com/ghostery/whotracks.me/blob/master/whotracksme/data/assets/2018-03/global/domains.csv#L5). As, with this change, it is very difficult to avoid visiting `google.com` domain as a first party, preventing this tracking in a vanilla browser would require disabling all third-party cookies. Alternatively, [Cliqz](https://cliqz.com/) and [Ghostery's](https://www.ghostery.com/) AI anti-tracking technologies block all third-party tracking cookies (Disclosure: the author works on this product). [Privacy Badger](https://www.eff.org/privacybadger) is also able to block third-party tracking cookies. ================================================ FILE: blog/government_websites_september.md ================================================ title: Government websites subtitle: If you are not the product, you're the taxpayer author: privacy team type: article publish: True date: 2018-10-10 tags: trackers, government header_img: blog/gov_trackers/gov.png redirect_url: https://www.ghostery.com/blog/government-websites-trackers +++ _This post is one of our regular monthly blogs accompanying an update to the data displayed on WhoTracks.Me. In these posts we introduce what data has been added as well as point out interesting trends and case-studies we found in the last month._
On WhoTracks.me we typically profile websites where we see presence of tracking. One new category of site we observed loading trackers this month was government websites. Government websites act as information portals, allowing citizens to access information or services from their government. In some cases the use of government sites will be mandatory, for example services set up for submitting tax or visa information. Thus, it is concerning that we see third-party tracking appearing on these sites, where users do not have a choice whether or not they access the service, and are then forced to hand over data to third-party companies by their governments. ![Average number of third parties per Country](../static/img/blog/gov_trackers/average_per_country.svg)

Average number of trackers seen on selected government websites from the WhoTracks.Me September dataset.

Here's a list of the government websites ending up in this month's release:
Country Site Notable trackers
Australia bom.gov.au Google Analytics, Doubleclick
Europe europa.eu Google Analytics, Google, Twitter
France ants.gouv.fr Google Analytics, Doubleclick
France legifrance.gouv.fr AT Internet
France impots.gouv.fr AT Internet
Russia zakupki.gov.ru Yandex
UK tax.service.gov.uk Google Analytics, Optimizely
US ca.gov Google Analytics, Google, AddThis
US dhs.gov Google Analytics, Doubleclick
US irs.gov Google Analytics, New Relic, AddToAny, Youtube, Foresee
US nih.gov Google Analytics, Doubleclick, Google
US noaa.gov Google Analytics
US state.gov Google Analytics, Google, Youtube, Qualtrics
US weather.gov Google Analytics, AddThis
It also surprised us that Germany (where the majority of our contributors reside) does not appear. A brief check of a few sites like bundestag.de and the Federal Tax Office shows a preference for self-hosted analytics, such as Matomo, rather than third-party solutions. Note that, as we do not collect data about pages with no third-party trackers, the data we show here may be biased for sites where sensitive areas do not have tracking. Further study would be required to access whether the tracking reported here leaks sensitive information when accessing public services. However, the presence of tracking on these pages is enough to leak valuable metadata about citizens to third-party companies. We should be asking if it is acceptable for our governments to expose us to this risk... ================================================ FILE: blog/how_cliqz_antitracking_protects_users.md ================================================ title: How Cliqz anti-tracking protects users subtitle: Using an algorithmic, data-driven approach to remove unique identifiers that track users. author: privacy team type: article publish: True date: 2017-07-22 tags: privacy, circle header_img: blog/blog-anti-tracking.jpg +++ There are already many tools available which aim to prevent the kinds of tracking we have described. In general these act as browser extensions which monitor network traffic and intervene when tracking is detected, i.e. blocking UIDs in transmission. These can be categorised into two groups: 1. Blocklist-based: These tools use curated blocklists in order to block third parties seen to be tracking. This predominately targets advertising trackers as an extension or side-effect to Adblocking features. Examples include Ghostery, Disconnect, Firefox tracking protection, and uBlock Origin. 2. Heuristic/algorithm-based: These tools use heuristic and/or algorithmic analysis to determine when to block or modify requests. Examples include Privacy Badger and Cliqz’s own anti-tracking system. Blocklist-based methods have several shortcomings. Firstly, blocking requests is very coarse grained and can easily break site functionality. Overly broad blocking rules may block many requests which are of no privacy risk. On the other side, exceptions made to prevent site breakage may then allow some privacy leaks, for example the Facebook like button is still allowed when using the [EasyPrivacy](https://easylist-downloads.adblockplus.org/easyprivacy.txt) blocking list, and there are [many other](https://easylist-downloads.adblockplus.org/exceptionrules.txt) such exceptions. Secondly, as these lists are manually maintained, they will always be playing catchup against trackers. Tracking companies can constantly change their URLs, domains and methods to prevent blocking, and those generating the lists must respond in each case. Finally, blocking lists bestow significant power to their curators. With blocking browser extensions being used ever more ([over 40% of users](https://downloads.pagefair.com/wp-content/uploads/2016/05/Adblocking-Goes-Mainstream.pdf) for some market segments), those who write the block list would have the power to cut off a significant proportion of a company’s traffic — deservedly or not. Heuristic approaches like Privacy Badger are limited by just having local knowledge. In many cases we will not know if data sent is unique to us until we have tested it in another browser and seen a different value, like in the fingerprinting example in the previous post. Thus some kind of collaboration is required between users to determine what data is safe, and what is not – and this is the method Cliqz’s anti-tracking uses. # Cliqz Anti-tracking As we outlined in the previous section, blocklists have several drawbacks, and we did not want such an aggressive system. Likewise, relying purely local evaluation of whether data is a UID or not has significant limitations. Therefore, we designed a system which combines local with global evaluation of tracking data. It is also designed to be conservative — we only remove data which we determine to be UIDs, and leave the rest alone. Our system modifies request URLs instead of blocking. This aims to reduce site breakage, and enable services to collect data, provided it does not compromise the user’s privacy. Like existing tools we focus on removing UIDs in transmission, rather than trying to prevent UID generation. Therefore we have three transmission vectors: HTTP Headers, URL Path and Post data. The latter we currently do not handle as our data shows that the reach of this method is very low, however our system allows us to continually monitor the situation, should this change. The Cliqz anti-tracking system is split into two subsystems. One that handles only Cookies, and the other which handles all other data sent in headers and the URL path. ## Cookie Protection Protecting from tracking cookies is relatively simple. This is because the vast majority of third-party cookies have no function beyond tracking. Therefore we can very simply strip these from the request without breaking the page. However, blanket third-party cookie blocking is not an ideal solution, because some third-party widgets do require cookies, to, for instance, authenticate with their service. In order to enable this use case, our system allows cookies in cases when user interaction with the widget is detected. When this happens, the third party is temporarily whitelisted to allow cookies. This implementation effectively prevents all cookie tracking, and rarely breaks the user-experience on web pages. This is despite the fact that over 96% of third-party cookies are removed. ## Unsafe Data Removal The second anti-tracking subsystem deals with the non-trivial problem of identifying whether the data sent in a request is ‘safe’ or not. By this we mean, that the data point could be used as a UID, i.e. it is unique to the user. Once we identify what data is unsafe we remove it from the request before it is sent by the browser. This means that if a tracker tries to aggregate their data using this UID, then all Cliqz users will appear as one, and thus will gain crowd anonymity. The algorithm therefore runs as follows: When a page is loaded, for each third-party request: 1. Analyse the URL, headers and postdata of the request. 2. Tokenise this data into key-value pairs. 3. Evaluate the safeness of each key-value pair. 4. If there are unsafe values, remove the data from the request. How can we determine what data is safe or not? A UID is characterised as a value which is unique to a single user, and which is repeatedly seen by this user. Such values can be detected by aggregating the data seen by multiple users over a period of browsing time. However, by the time this aggregation would tell us what the UIDs were, it would be too late — the trackers would already have the data. Therefore, our algorithm does the inverse: Detecting the values which cannot be UIDs, and removing all other data. The advantage of this method is that the protection for new users is available straight away. New UIDs will not be known by the system, and therefore be removed by default. Furthermore, the set of safe values is significantly smaller than the set of unsafe. Safe values will be categorical in nature, and therefore be only ever be `O(1)` in size, while the set of UIDs will be `O(n)`, where n is the number of users. We can also classify many values as safe locally, without having to consult the global safe value set. Our system uses the following rules for local classification: - If a value has not been seen previously for the `(third party, key)` pair, it is safe. - If a value is too short, i.e. has too little entropy to be a UID, it is _safe_. - If more than 3 different values have been seen for a `(third party, key)` pair over a two day period, then the value is not persistant, and therefore safe. If none of these rules are able to classify the value as safe, we use the global safe set, which tells us which values have achieved a quorum of users who all saw the same value. ### Example Consider a hypothetical visit to the site `example.com`, which has tracker.de as a third party. After processing a request we generate a set of `T = [(s, d, k, v)]` tuples as follows: ```javascript T = [ (s= example.com, d= tracker.de, k= z, v= 1459866821), (s= example.com, d= tracker.de, k= fl, v= 21.0), (s= example.com, d= tracker.de, k= u, v= CCAAAABI), (s= example.com, d= tracker.de, k= vr, v= 1440x1024), (s= example.com, d= tracker.de, k= c7, v= e9d4a7e4d2185cec), ] ``` We can then evaluate these values: - `(s= example.com, d= tracker.de, k= z, v= 1459866821)`: This was the first time we saw the value `1459866821` for the given `(d, k)`, so this data is _safe_. - `(s= example.com, d= tracker.de, k= fl, v= 21.0)`: The value `21.0` is too short to be a UID, so it is _safe_. - `(s= example.com, d= tracker.de, k= u, v= CCAAAABI)`: More than 3 different values seen in the last two days for the same `(d, k)`, so _safe_. - `(s= example.com, d= tracker.de, k= vr, v= 1440x1024)`: Always the same value seen for this `(d, k)` pair, however the value is in the global safe set (as it represents a common screen resolution), therefore _safe_. - `(s= example.com, d= tracker.de, k= c7, v= e9d4a7e4d2185cec)`: Always the same value seen, and not in the globally safe set, so this value is _unsafe_ and will be removed. ### Building the Global Safe Set We build the global safe set daily using the data sent from users’ clients. Clients collect the tuples of data from each request while browsing and send this back to us every hour, adding a timestamp parameter. The values in the tuple are hashed to prevent user-identifiable information being sent to us. The client guarantees that a maximum of one message is sent per user per hour, so this can be used instead of a user id to count the number of users for each (third party, value) tuple. If the number of users exceeds the quorum threshold for a given hour, it is added to the safe set. This model allows users to create this collaborative safe value set without compromising their privacy. Any privacy sensitive information in the data is obfuscated by the hash function, and, as no UID is required for our aggregation, we cannot derive any browsing history beyond the single hour granularity (there is no way to link messages from different hours), and the most information that could be gained is the first-party domain names visited. This protection is further strengthened by the use of our [Human Web](https://events.linuxfoundation.org/sites/events/files/slides/collecting-user-data-socially-responsibly.pdf) technology which further obfuscates the source of each message we receive. # Cliqz vs Other Anti-tracking Systems The algorithmic, data-driven system for removing UIDs from third-party requests which we have described has several advantages over other anti-tracking solutions. As an online system we can respond much quicker, and without human oversight, to changes in trackers and their techniques. If a tracking company switched domains to try and avoid blocklists, we would have the data to block this tracking within a day. Human-curated blocklists would take comparatively longer to update. In our [paper](https://static.cliqz.com/wp-content/uploads/2016/07/Cliqz-Studie-Tracking-the-Trackers.pdf) we did several tests to measure the difference between our system and other blocklist systems. Our tests indicated a reduction in breakage on web pages caused by our system, compared to adblockers. We also saw that blocklist-based systems blocked more often, but a large proportion of these blocks were false positives: requests which did not contain any UIDs. The downside of our method is that, unlike other blockers, we see a net performance loss when loading complex web sites. This is because, other blocking systems simply block the javascript which will then attempt to calculate and send a fingerprint, while we will block just the outgoing request with UID. Thus blockers get a performance benefit of avoiding running this resource-heavy tracking javascript code. However, we believe that in the long term, this property is a net benefit. Unlike other anti-tracking systems, Cliqz can forgive. If a tracker updates its code, and switches to a method which no [longer sends UIDs](http://www.slideshare.net/jmpujol/data-collection-without-privacy-sideeffects-at-big2016-www-2016), then our system will immediately stop blocking their data. With blocklists, there is no such mechanism — trackers are then incentivised to circumvent the block (for example using a new domain name), rather than improving their data collection methods. # Summary Our anti-tracking system has now been running successfully for over a year, blocking around 300 million cookies and removing around 10 million UIDs per day to keep our users free from tracking. The same system, as described here, is now also available on [our mobile browsers](https://cliqz.com/en/mobile) to provide the same protection across even more devices, and give users control over what data third-parties can collect about them. ================================================ FILE: blog/how_facebook_knows_exactly_what_turns_you_on.md ================================================ title: How facebook knows exactly what turns you on subtitle: A technical analysis of the methods used to track users as a third party. Deep dive into a couple of case studies. author: privacy team type: article publish: True date: 2017-07-22 tags: privacy, circle header_img: blog/blog-facebook.jpg +++ The modern web is built around advertising. A multi-billion dollar industry ([$42bn in 2013 in the US](http://www.iab.net/about_the_iab/recent_press_releases/press_release_archive/press_release/pr-041014) and about [6bn in 2015](https://de.statista.com/statistik/daten/studie/456157/umfrage/umsaetze-im-markt-fuer-digitale-werbung-in-deutschland/) in Germany) primarily concerned with one question: How to make the most money by serving exactly the right ad, at the right time, to the right user. Web pages are extremely complicated constructions, often meshing together multiple software tools and services from different providers, from analytics and social sharing widgets, to dynamic advertising and content recommendation engines. Consider an average news site with social media sharing buttons. More often than not, these are created by linking to scripts from Facebook, Google, Twitter, etc., which then inject the required content into the page. These third parties may then in turn load other required services into the page. In isolation, this seems mainly harmless. Services are being provided to the website owners to better integrate third-party services such as social networks, add extra widgets such as comment sections and related content, and improve the website’s monetisation through targeted advertising. However, the implementation of these services often cause a privacy side-effect: they allow third-parties to track your web-browsing across the web, and in some cases even link this history to you personally. When a user visits a new site the third parties included in the page can then look up the browsing history they have collected for this user, and then generate a personalised response based on this information. This is akin to being given a personalised newspaper where the adverts have been selected based on which articles you have read previously, in both this and other newspapers, any magazine articles you might have read, where you shop and what items you were looking for, [where you bank](http://cliqz.com/magazine/pressemitteilung-cliqz-tracking-beim-online-banking) and more. Our data shows that largest of these tracking third party services can be seen on [almost half of all pages you might visit](http://josepmpujol.net/public/papers/pujolTrackingTheTrackers.pdf), and many others share and [trade user data](https://big.exchange/) amongst each other in order to build a comprehensive user browsing history. Luckily, as the web is an open system, we can see what these companies are up to, and equip the browser with the capability of foiling their attempts to send tracking data. This post is the first part of a two-part series. In the second part we will describe how our Anti-tracking system works. This part acts as a background to that, describing the how and why behind online tracking. The methodology and data we present here is based on our [published work](http://josepmpujol.net/public/papers/pujolTrackingTheTrackers.pdf) on Anti-tracking, which we [presented](http://www.slideshare.net/jmpujol/tracking-the-trackers-www-2016) at WWW2016. ## How online tracking works The mechanism behind online user tracking is simple enough. First, one must be a third party to many page loads across the web. Our data shows that almost 30% of web sites require 10 or more different service providers to fully load their content, and dynamic advertising alone can bring this many different companies into the page. Secondly, the request a third party receives when loaded into a page should contain some kind of user identifier (uid) for the client visiting the page, as well as the address of the first party page visited (usually provided by the Referer [sic] header). Collecting together the first-party pages seen for each uid will then yield the browsing histories of all the users seen. A simple list of visited web pages may not seem like a significant privacy violation to some, however further analysis can yield much more information than one might expect. Trackers can collect users’ browser and operating system, which can be used for [price discrimination](http://news.northeastern.edu/2014/10/ecommerce-study/), and rough geographical location can be checked using [IP geolocation](https://en.wikipedia.org/wiki/Geolocation). One can also find private urls in the history to determine membership of certain services, such as some [online banking portals](https://static.cliqz.com/wp-content/uploads/2016/07/Cliqz-study-tracking-in-online-banking.pdf) which contain trackers. Another example is the twitter analytics dashboard (e.g. analytics.twitter.com/user/sammacbeth/home). This url is only accessible when logged in as a specific user, and when accessed the browser will transmit this user name in the url to the trackers in this page (in testing, these included Google, Microsoft and tellapart.com), thus enabling these services to add a user’s twitter handle to the previously collecting browsing history. Private urls, such as this, are particularly dangerous, because they often contain Personal Identifiable Information (PII) which puts a real identify on the other urls that are being collected in that session (See [http://www.slideshare.net/jmpujol/data-collection-without-privacy-sideeffects-at-big2016-www-2016](http://www.slideshare.net/jmpujol/data-collection-without-privacy-sideeffects-at-big2016-www-2016) for an example.). ### UID Generation Techniques The uid that trackers need in order to attribute page loads to specific users can be generated in several different ways: 1. Cookies – This is the simplest and most common method for generating uids. Cookies are a web standard for sharing state between a client and server over the stateless HTTP protocol. It is an important part of the web, which enables sites to keep track of your login and/or preferences between visits. Cookies work as follows: When a client makes a request to a server, in the response the server can set a header Set-Cookie with a value of its choosing. The client will, from then on, send this value in headers for any subsequent requests for this domain, and thus the server will know which user it was who sent the request. 2. Network fingerprint – This method uses the properties of the network from which the request comes from as an identifier, usually the IP address. This varies in effectiveness based on whether users have unique IPs or not. 3. Client fingerprint – Here, code is run in the client browser to try and build a unique identifier from data accessed in Javascript, Flash and other APIs, for example installed fonts, browser plugin versions, screen resolution, browser version and more. Techniques such as canvas fingerprinting are further able to fingerprint the specific hardware configuration of the user’s computer. Together this can generate a unique fingerprint which is stateless, and endures even when private data is cleared, and private tabs are used. Once generated, these uids must be transmitted to the tracker with information about the page the user is visiting. Again there are three primary methods: 1. HTTP Headers – This is metadata send along with a request with information for the server. This is where Cookies are transmitted, but also other data can be sent here. Our data shows that 45% of requests to third parties on web pages seen by our users contain a cookie header. 2. URL Path – Arbitrary data can be sent in the URL path requested from the server. This is commonly in the form of a query or parameter string — key/value pairs separated by & or ; characters at the end of the query. 52% of third party requests have some kind of query string, and 1.5% a parameter string. 3. Post data – This is data sent from the client as part of the main body of the request. We see this kind of request in 0.05% of cases. ### Case Study 1: Facebook cookie tracking Facebook use cookies to link your web-browsing behaviour to your Facebook account. Facebook widgets are embedded in various sites around the web, and will send the address of the page you are viewing along with your Facebook cookie, enabling Facebook to build a list of sites you have visited. Our data shows that Facebook’s widget reaches 25% of pages loaded by our users – this means that Facebook could collect 25% of an average user’s browsing history. We can see this tracking in action by inspecting requests in the web browser. First, if we visit the Facebook home page, we can see a cookie called datr being set: Now, upon visiting a site which has a Facebook widget, in this case bild.de, we can see a request to facebook.com. As third-party cookies are enabled in the browser (the default setting in all major browsers), we will send the cookie we got on the previous page along with the request. The Referer header of this request will also contain the site I am visiting: www.bild.de. As I continue to browse the web, this process will repeat, and Facebook will collect a series of requests with this datr cookie and the pages I was viewing. Finally, if I now log into my Facebook account, we see that the datr cookie remains, and now alongside a cookie with my Facebook user ID. This means that Facebook can now attribute all the pages I have viewed with my personal Facebook account. This mechanism allows Facebook to collect your browsing habits across the web, in order to tailor adverts and recommendations within their site. [Our measurements](http://josepmpujol.net/public/papers/pujolTrackingTheTrackers.pdf) show that this tracking covers around 25% of pages visited by our users. Facebook were banned last year from using this tracking on European users who had not logged into their site, however this was recently [overturned](http://www.theverge.com/2016/6/30/12069626/facebook-belgian-privacy-commission-cookie-user-tracking-case-overturned), so this practice continues. ### Case Study 2: Moatads fingerprinting [Moat](https://moat.com/) is an analytics and advertising provider. They are present on many popular news sites, where their JavaScript is loaded into the page, and then a tracking pixel is sent back to their servers. We can observe this behaviour by opening two different sites in our web browser and inspecting the requests to moatads.com: Here we can see many parameters are sent in the request, and many values match across both requests. However, we cannot know for sure if these represent uids, or just other values used legitimately for the service. However, the qn value is suspicious, as a long cryptic value which remains the same when visiting different sites. We now try opening the same sites in a different browser: Again, pixels are generated with various parameters set in the request URL. Some are the same as we saw in the first test, for example the qq parameter. However, looking at the qn value we see that it is again the same on both web pages, but different to the value we saw on Mac. We can hypothesise that this is a fingerprint of this browser which functions as a uid, however we would need more examples from more unique browsers to properly test this. Finally, we test the qn in a private tab in the first browser. As shown below, we see that the same fingerprint is generated. Therefore, Moat are able to also tag page views in private tabs with the same uid as in a normal window, suggesting that they can bypass this protection for their tracking purposes. # Where are the trackers These two case studies have shown the technical means with which companies can collect the pages you visit, and group them by a particular user, be that against a specific facebook profile or just a hash value which uniquely identifies one’s computer. Having established that third parties may snoop on some pages you visit on the web (with the first party’s permission), the question is how far does this tracking reach, and how much of our browsing habits can these third parties collect? We presented our data on the [online tracking](http://josepmpujol.net/public/papers/pujolTrackingTheTrackers.pdf) seen by 200,000 users over a two week period at the [WWW2016](http://www2016.ca/) conference in April, which analysed over 13 million page loads by our users. A large study of 1 million sites has also been done by researchers at Princeton with similar findings to ours, although the study is not based on real user traffic but rather on data collected by instrumented browsers that download and scan for trackers top sites on the Web. We present some updated results from our on-going browser telemetry, during August 2016, and containing over 140 million page loads over 1.8 million unique domains. Multiple visits to the same site and/or page are counted multiple times, thus the data set weighs more popular pages more strongly, and represents the tracking observed by an average user of our browser. The first result we observe from our data, is that a small collection of third parties are are installed in a huge number of visited pages. From a list of 2000 domains, representing the top tracking domains, we see that 96% of page loads include a request to a third party in this list. Over 80% of these page loads (and 78% of the total) contain some kind of tracking attempt. Thus, a user browsing the web with no tracking protection could be tracked on 78% of the pages they visit. We can further look at how much of an average user’s browsing history each third-party company might be able to tracking. We analyse the ‘reach’ – the proportion of total page loads in the data set seen – by domains associated with particular companies or products. The figure below shows the top companies in terms of total reach, and for each we indicate the types of behaviour seen on each page. ‘Safe’ means that no tracking behaviour was seen, just that a request was made to the domain; ‘cookie’ and ‘qs’ mean that there was an attempt to transmit a uid with one of these methods, and ‘both’ means that both methods were used. The figures show that the big players – and particularly Google, with their products taking the top three places – have significant reach across the web. Some specific company behaviours can also be observed, for example Google Analytics does not use cookies, using a weaker kind of fingerprint. Also, Amazon, offer CDN services on their cloudfront.net and amazonaws.com domains, thus a high proportion of their reach is safe. The other feature of the tracking landscape is the long tail of tracking companies. There are 27 companies/services with over 5% reach, 110 with over 1%, then 450 over 0.1%. This 0.1% still corresponds to over 140,000 pages seen on this data set. Finally, we can look at how many trackers are seen on each page load. The figure below shows how many distinct tracking domains were contacted for page loads in the dataset. We see that over 10% of pages have over 20 different trackers in them, and the vast majority of page loads have multiple trackers. Therefore, not only are users tracked across most of the web, after there are many companies who are able to generate comprehensive user profiles. # Conclusion In this post we’ve given a general description of how online tracking works, and looked at the extent of tracker companies’ reach across the web. In the next post we will look at how we can stop this tracking, and give an in depth description of how our Cliqz Anti-tracking technology works to prevent tracking without an adverse effect on user experience. ================================================ FILE: blog/manifest_v3_privacy.md ================================================ title: Chrome's Manifest V3 - Improving Privacy? subtitle: How Chrome's changes will reduce user privacy author: privacy team type: article publish: True date: 2019-06-18 tags: blog, extensions, privacy, chrome header_img: blog/adblocker-perf-study.jpg redirect_url: https://www.ghostery.com/blog/manifest-v3-privacy +++ The Chrome team's proposed changes to browser extension APIs, known as Manifest v3, have proven controversial due to their expected impact on adblockers and privacy extensions. Of particular concern are the changes to the `webRequest` API, whose blocking capabilities are being replaced by the `declarativeNetRequest` API. In repeated posts the Chrome team claim that these changes are required to improve the *performance*, *security* and *privacy* of extensions. In a [previous post](./adblockers_performance_study.html) we showed that, for the most popular adblocker engines, performance is already very good, and these changes are unlikely to improve much. In this post we assess the privacy argument for the changes to request handling, if the proposed changes do improve privacy, and how Ghostery specifically will be affected. We find that: * The Chrome team have only belatedly stated specific privacy concerns with the `webRequest` API, and these are still not included in the design document. * The proposed changes do not provide any protections against the stated privacy issues. * Privacy extensions like Ghostery will be negatively impacted by the changes, reducing their ability to keep users safe online. ## Extension privacy Browser extensions have the potential to cause many privacy problems - when granted permissions, they can see every page you visit in the browser, view their contents, read and write form data, and send requests to any server on the internet. These powers are required for some of the valuable features extensions provide. Therefore, as the Chrome team rightly [point out](https://blog.chromium.org/2019/05/taking-action-on-deceptive-installation.html), ensuring extensions are consentfully installed is the first step to address privacy. The Manifest v3 changes, however, primarily address extensions' capabilities post install. As privacy at this point is also a stated goal, what are the privacy concerns and attacks that the changes seek to address? In the Manifest V3 [design document](https://docs.google.com/document/d/1nPu6Wy4LWR66EFLeYInl3NzzhHzc-qnk4w4PX-0XMw8/edit#heading=h.9lwe237fxtp2) this goal is stated as follows: > Users should have increased control over their extensions. A user should be able to determine what information is available to an extension, and be able to control that privilege. Later in the document the changes to the `webRequest` API are described, but only using a performance-based reasoning: > … the extension then performs arbitrary (and potentially very slow) JavaScript, and returns the result back to the browser process. This can have a significant effect on every single network request, ... They also acknowledge that the `webRequest` API should remain in place for observation. > The non-blocking implementation of the webRequest API, which allows extensions to observe network requests, but not modify, redirect, or block them (and thus doesn't prevent Chrome from continuing to process the request) will not be discouraged. This implies that the potential privacy impact of extensions being able to observe all requests going out of the browser are not a concern for these API changes. While the `webRequest` API remains, the switch to allow blocking only via the `declarativeNetRequest` API does nothing for the stated privacy goal of increasing user control over the information extensions can access. Despite this, since [our study](./adblockers_performance_study.html) showed that the performance cost of `webRequest` blocking for leading adblockers was not an issue, the Chrome team have focused on privacy reasons for the changes. In their [recent blog](https://blog.chromium.org/2019/06/web-request-and-declarative-net-request.html) about web request and declarative net request changes, they state: > In order to improve the security and privacy guarantees of the extensions platform, we are rethinking some of the extension platform's core APIs. That's why we're planning to replace the blocking Web Request API with the Declarative Net Request API. This shift in angle has also come up in public statements by Chrome devs: > "… The big problem with webRequest is unfixable privacy and security holes. …" @justinschuh ([Source](https://twitter.com/justinschuh/status/1134060703231254528)) In the blog post they also mention one potential malicious use of webRequest: > Because all of the request data is exposed to the extension, it makes it very easy for a malicious developer to abuse that access to a user’s credentials, accounts, or personal information. If this is the single privacy loophole the `webRequest` changes are targeting, then it seems strange that the solution is to remove the blocking capabilities of `webRequest` and leave the observational ones. Post Manifest V3, the exact same malicious extension will be possible. We can imagine that the Chrome team's strategy may be, that by providing a simple alternative API for blocking use-cases, the extension review process can be tougher for extensions asking for `webRequest` permissions. This, however, would also be possible by just introducing the new API, leaving `webRequest` as it is, and providing developer incentives to switch unless they really need `webRequest` for their use-case. It is strange that this privacy issue was not stated in the original design document, and the proposed change to `webRequest` is seemingly just collateral damage that does not address the stated goals. More transparency is needed on what the strategy is here, and why keeping `webRequest` observation with blocking removed should be the solution. To summarise: - The stated privacy improvements of Manifest V3 are addressed elsewhere in the proposals. - The privacy and security issues with `webRequest` blocking have not been fully articulated by the Chrome team, with only a brief mention of malicious behaviour in a blog post last month. - The removal of `webRequest` blocking does not improve the privacy of extensions. Therefore at this point, the primary impact on privacy from the proposed changes will be the neutering of the capabilities of several privacy extensions. Privacy Badger devs [expect their core functionality to be broken](https://github.com/EFForg/privacybadger/issues/2273) by the changes. Similarly, we expect it to be difficult to provide the same level of protection in Ghostery should these changes come into effect, and we will describe why in the rest of this post. It is ironic that a change ostensibly aimed at improving user privacy will actually reduce it for many users who rely on privacy extensions to protect them online. Some have suggested that the changes simply align Chrome with Apple's Safari, which provides a similar declarative blocking API for extensions. This overlooks the fact that Safari comes with significant privacy protections by default, having been blocking most third-party cookies by default for years, and recently bringing in advanced anti-tracking measures in the form of [ITP](https://webkit.org/blog/8613/intelligent-tracking-prevention-2-1/). Chrome on the other hand, ships with zero tracking protection by default, and is now hindering extensions which try to provide comparible protections to other browsers. ## How removing webRequest blocking affects Ghostery This analysis is based on the `declarativeNetRequest` [API documentation](https://developer.chrome.com/extensions/declarativeNetRequest) as of 17th June 2019. The primary features of the API are: 1. A matching grammar for specifying rules that will trigger blocking, header modification or redirects. 2. Up to 30,000 static rules per extension 3. The ability to add _dynamic_ rules at runtime, up to a maximum of 5,000 rules. 4. Rules can have a white- or black-list of first-party sites, to control triggering. 5. Individual sites can be dynamically whitelisted, up to a maximum of 100 per extension. Ghostery contains the following components which will be affected by the webRequest API changes: ### 1. Tracker matching and blocking Ghostery contains a blocklist of over 4,000 filters which are used to detect and block trackers. The extension allows users fine-grained control over these, allowing or blocking specific trackers on specific sites or globally. The list of detected trackers is shown in the Ghostery UI for each page visited. To support the `declarativeNetRequest`, these 4,000 filters would have to be re-written to the new filter grammar that Chrome offers. We are likely to lose some filters in the process, as certain types of matching rule, for example Regex's likely cannot be implemented in the more restrictive grammar. The more challenging issue, however, is maintaining Ghostery's rich configurability with the low threshold of dynamic rules allowed. As every rule should be toggleable, all 4,000 filters would have to be _dynamic_ rules. This means that we are already using 80% of our allowance from the start, before we have even started adding supplementary rules for adblocking and cookie blocking. Likewise, the limit of 100 whitelisted sites is prohibitively low, as many users may us the Ghostery 'Trust Site' feature for more sites than this. It is unclear how to handle hitting this limit, as to the user it will seem like the feature is broken if they trust a site, but it does not get saved. Furthermore, the new API, in it's current form, does not report the results of blocking back to the extension. This means that we will still have to run our filters on all urls via the `webRequest` API anyway, in order to display the list of trackers seen and blocked. This means that the user pays the cost of keeping the block list loaded in memory and matching against each url twice. ### 2. Cookie blocking The Ghostery extension uses a heuristic third-party cookie blocker as part of the 'Enhanced Anti-Tracking' feature. This feature blocks third-party cookies in most cases, using a set of heuristics to decide when cookies should be allowed. It is currently not clear if these heuristics will be able to work correctly without the webRequest API, nor if the dynamic filter cap is sufficient to even hold the basic cookie blocklist. Our cookie heuristics respond to user input, for example clicking on a Facebook like button or Google login form, in order to trigger a temporary cookie whitelist for a specific domain. To implement this with `declarativeNetRequest`, we would have to add or modify our cookie blocking rule temporarily. As the API for this is asynchronous, we introduce a race condition that we did not have before. If the rule is not added before the request we want to whitelist, the mechanism will fail. This can, for example, break Google logins on third-party sites. The cookie blocking is done based on a dynamically generated list of tracker domains of between 2,000 and 3,000 entries. For these domains, third-party cookies should be blocked, unless a heuristic allows it. Again, the limited rule threshold of the `declarativeNetRequest` API means that this list would have to be reduced. Another concern is that the [Rule condition specification](https://developer.chrome.com/extensions/declarativeNetRequest#type-Rule) can distuingiush between `firstParty` and `thirdParty` contexts for a request, but this is done on a frame level, rather than relative to the page document. This means that we would not, for example, be able to block Google cookies inside a Google Ads iFrame, as in this context the API would consider requests from the frame as first party. ### 3. Removing private data points The other component of Ghostery's 'Enhanced Anti-Tracking' feature is the dynamic removal of url parameters seen to be used for cross-site tracking. This uses a [k-anonymity](./how_cliqz_antitracking_protects_users.html) based algorithm, using anonymously contributed data from our users. As the `declarativeNetRequest` API does not support dynamic redirects, this component cannot be implemented with it. ### 4. Adblocker Ghostery includes an additional adblocker component which is able to further block ads based on standard blocklist. As this feature should also be toggleable on-and-off at runtime, we would need to use _dynamic_ rules for these filters. With only 1,000 rules available after adding the Ghostery tracker matching, the coverage of this feature would be drastically reduced. ### 5. WhoTracks.Me Data Ghostery is the primary source of data for this website, using our [anonymised telemetry system](https://arxiv.org/abs/1804.08959) to report on global tracker trends. This largely relies on the webRequest API in order to observe which trackers are on which page. Changes caused by the introduction of `declarativeNetRequest` will reduce the quality of this data. Namely, cookies blocked by the declarative API will not be visible to webRequest listeners. This means that we will not be able to distinguish between trackers setting cookies, which are then blocked, and those who do not set cookies. ### Summary To summarise, the Manifest V3 changes to the webRequest API will require a significant re-write of the Ghostery extension to be able to fit the existing features into the constraints of the `declarativeNetRequest` API. The result will be: - Slower: URL matching will have to be done twice in order to show tracker counts in the UI. - Less configurable: Configuration may have to be limited to fit within the very low dynamic rule limit. - Break sites more often: We will have to evaluate the trade-offs of relaxing the third-party cookie blocking vs. breaking sites. - Less private: As the private data removal feature will have to be removed. ## Conclusion In this post we have shown that the current proposed changes to the webRequest API by Chrome do not improve privacy, and in fact reduce it, by severely hindering the operation of privacy extensions like Ghostery. The limitations on dynamic rules in the new `declarativeNetRequest` API are particularly taxing for extensions which aim to the give user control over what is blocked and what is not. This forces extensions into a 'dumb blocker' model, where block lists are fixed, and the only controls are an on/off toggle. At the same time, the changes increase the difficulty and practicality of implementing dynamic heuristic mechanisms for detecting and blocking tracking. The webRequest API powers much innovation in browser extensions, however it does implicitly provide access to private user data. While the Chrome team state that privacy is a reason for the proposed changes to this API they have not stated which specific concerns they aim to address. The Manifest V3 changes do not prevent extensions accessing private user data via webRequest, nor have other potentially dangerous APIs like content scripts been limited. Therefore the claims that this change improves extension privacy are misleading and disingenuous. The fact that very few of the initial concerns regarding Manifest V3 have been addressed in the months since the original announcement, means that it currently looks like the changes will be forced through, despite community objections. This means that Chrome users will become second class web citizens with regards to their access to tracking protection. This is however just a continuation of a trend where Chrome stands still or actively reduces privacy while the rest of the competition have been pushing forward. At this point we recommend considering switching away from Chrome, if you haven't done so already, to browsers with privacy built-in by default. For example, the [Cliqz Browser](https://cliqz.com/en/download) has Anti-tracking built in and enabled by default, and Firefox now ships with [tracking protection on by default](https://blog.mozilla.org/blog/2019/06/04/firefox-now-available-with-enhanced-tracking-protection-by-default/). _Disclosure: WhoTracks.Me is a joint effort by Cliqz and Ghostery._ ================================================ FILE: blog/private_analytics.md ================================================ title: Tracking visits without tracking people subtitle: A privacy-by-design approach. author: privacy team type: article publish: True date: 2018-05-03 tags: analytics, privacy-by-design header_img: blog/analytics/analytics.png redirect_url: https://www.ghostery.com/blog/private-analytics +++ Analytics are one of the most common use-cases on the web. You want to know how many people are visiting your website, whether anyone actually clicked the link you posted on social media, or who is sending traffic to your website. For most sites, the solution is to just drop a [Google Analytics](../trackers/google_analytics.html) script into the page - it's free, after all... This has led us to the current situation, where we see Google Analytics having presence across 87% of the top half a million websites, and, despite using reasonably short-lived identifiers, the way the data is collected can be used to [track users across these sites](https://www.slideshare.net/jmpujol/data-collection-without-privacy-sideeffects-at-big2016-www-2016#13). Is counting page visits such a difficult problem that only Google has solved it? No, there are [paid](https://get.gaug.es/) and [open source](https://matomo.org/) alternatives available, but why pay when you can use a free version which does more, and why host a server with the extra costs that entails, when you don't have to? But is Google Analytics actually better than the competition? We would argue that, at least among privacy conscious users (i.e. those [who contribute to the WhoTracks.Me dataset](../blog/where_is_the_data_from.html)), Google Analytics will report vastly incorrect figures, for two main reasons: 1. Our data shows that on 29% of pages with Google Analytics some of the requests will be blocked due to Ghostery blocking settings. 2. On 19% of pages with Google Analytics, Cliqz and Ghostery's AI anti-tracking will remove potential identifiers from the request, often causing unique visitors and conversions to be incorrectly measured. ## Analytics without tracking So how can we _accurately_ measure the traffic coming to our site without exposing the user to tracking and privacy side-effects? This was a problem we faced when we created the WhoTracks.Me website. We wanted to have _some_ analytics so that we can measure if we are being successful in engaging people with the information we are providing on the site. However, we had a few constraints: 1. No tracking. We [define tracking](../blog/what_is_a_tracker.html) as when a service is able to collect and correlate data across multiple sites. Unfortunately, as server-side aggregation is the norm amongst third-party analytics providers, privacy cannot be guaranteed. [Client side alternatives](http://josepmpujol.net/public/papers/big_green_tracker.pdf) have been proposed, but unfortunately [the implementation](https://github.com/cliqz-oss/green-analytics) only reached a proof-of-concept state. This means we have to roll our own service. 2. Minimal Ops. WhoTracks.Me is a statically generated site, which is simply hosted on a CDN. This decision was made to minimise costs, make it fast, and eliminate the need to deploy and monitor hosting infrastructure. Having done this, it does not make sense to have to deploy infrastructure in order to host a [Matomo](https://matomo.org/) or similar service. 3. Respect Privacy. The system should not store any personal information from users (i.e. IP address), nor be able to correlate visits for an individual user over a long time frame. Apart from the obvious reasons for this, it makes regulatory compliance easy: If we do not hold IP addresses, it is not possible for us to extract data on an individual user for data access or deletion requests (as per GDPR). Our analytics implementation satisfies these three constraints, using probably the oldest technique on the Internet: server log parsing. Daily analytics for the WhoTracks.Me site are generated as follows: 1. Visits to the site are logged via [CloudFront's logging mechanism](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html). 2. Each day, a script processes these logs, to obfuscate personal data such as IP addresses. This script generates a random key for the day, and encrypts all IP addresses with this key. The anonymised logs are copied to a new bucket, and the key is destroyed once the job completes. This method allows us to count unique visits from an IP address during a single day, but no day-to-day correlations can be made, nor can the IP address ever be recovered from the anonymised value. 3. The original CloudFront logs (with IP addresses) are removed. 4. We can then parse the clean logs and filter out requests to static resources and those by bots in order to see requests to actual pages. We can count unique visitors within single days, using a combination of user-agent and anonymised IP; we can see where incoming traffic is coming from via HTTP referrers (which we also strip of potentially revealing parameters) and so on. ![Diagram of Log Sanitisation](../static/img/blog/analytics/sanitise_access_logs.svg)

Processing of raw CloudFront logs to remove potential personal data.

This workflow allows us to keep track of how much traffic we are getting to the WhoTracks.Me website. There is also no reason that this method could not be scaled up to more complex use-cases which services like Google Analytics provides, like conversion counting - provided the time frame that this conversions can occur in are shorter than the time the IP encryption key is used for. The method is also safe with respect to privacy regulations and user preferences. As IPs are stored for maximum 1 day (and this is only because CloudFront's logging does not obfuscate IPs for us), no other personal information is collected, and message linkage limited to 1 day, there are no additional obligations regarding the usage of this data under GDPR. Furthermore, as tracking is time limited and context limited (this data can only be used for usage on whotracks.me), it respects [Do Not Track](https://en.wikipedia.org/wiki/Do_Not_Track) automatically (using the standard's own [tracking definition](https://www.w3.org/TR/tracking-dnt/#terminology.activity)). ## Conclusion We rolled our own analytics for this site because there was no off-the-shelf solution providing the (very basic) analytics we wanted without significant extra overhead, or potential privacy implications for users of the site. Our system leverages CloudFront logging with a data obfuscation step in order to collect privacy-safe server logs which can then be analysed for basic insights. This technique could be extended to provide most of the richer features of existing web analytics tools. The lack of privacy-preserving tools in the web analytics ecosystem is a worrying trend. Google Analytics dominates as they provide an extremely feature-rich product as zero cost to the webmaster. It is difficult to see how a service can compete with free without selling analytics data. Existing competitors mostly aim for businesses who will pay for a premium product, and leave bloggers and smaller sites to Google. While increasing use of adblockers is a more fundamental threat to Google's Ad business, a side effect may be a loss of trust in Google Analytics, as we measure [29%](https://github.com/ghostery/whotracks.me/blob/master/whotracksme/data/assets/2018-03/global/trackers.csv#L2) of pages with Google Analytics being affected by blocking. We already see companies which rely on analytics for core business activities (for example advertisers using affiliate schemes) deploying multiple analytics scripts and averaging the results. If the trust in analytics breaks down, then this whole ecosystem may unravel. ================================================ FILE: blog/static_site.md ================================================ title: Building whotracks.me subtitle: Adding search, data, plots and blog to 1000+ pages of tracker profiles and top domains. author: privacy team type: article publish: False date: 2017-11-03 tags: tracker-free, lightweight header_img: blog/blog-site.jpg +++ At Cliqz and Ghostery, we [collect anonymous data about trackers](/blog/where_is_the_data_from.html) to power our [anti-tracking](blog/how_cliqz_antitracking_protects_users.html) technology. We see our anti-tracking as a community effort and as such we want to share a structured representation of this data to cast some light on the tracker landscape. Out of the three main entities involved in a page load: **users**, **websites** and **trackers**, we have data only on the last two. We'll start with: * Profiles of the [top 500 trackers](/trackers.html) * Tracker data on the [top 500 domains](/websites.html). With these out of the way, a blog space would be needed. This for two of reasons. We realised there was a need for a learning space where we explain concepts referred to in the site. We call these **primers**. These define what we call a [tracker](/blog/what_is_a_tracker.html), what [cookies](/blog/cookies.html) and [fingerprinting](/blog/fingerprinting.html) are or [where this data comes from](/blog/where_is_the_data_from.html). Hopefully over time it will become a space for curious readers to be introduced to tracking technologies. The second reason is to have a space where we'll be writing about particular trackers, technologies, papers, engineering, and other interesting topics. ## Going static Through whotracks.me, we want to cast some light on the tracking landscape, but also make a point about trackers and **privacy by design**, hence the choice of this being a static site was pretty obvious. This meant that we could build the whole site offline, put it in a folder and serve it through CDN. Given this will be updated a few times a month, build performance was not really a big issue for us. But stumbling upon [a discussion](https://news.ycombinator.com/item?id=15507538) about site generators' performance, some comments read: - *"with Hugo + Pygments was taking ~20s for ~20 pages at the time"* - *"92 pages in 1s (full rebuild, No CSS magic tooling though)"* - *"Rust: ~10k pages in ~60s"* The assumption would be that most of this time is spent parsing the markdown files. To build this site however, with the exception of the blog, the rest of the pages are mainly about instantiating a template, plugging some content, and writing to disk. So most likely a comparison between site generators and this would be unfair. At the time of writing, whotracks.me has roughly 1020 pages. On 1000 of these pages there are offline generated plots, quite some data and a fair amount of tooling with respect to styling. On a `Thinkpad x230` with an Intel `i3 processor`: ```bash (venv) ➜ whotracks.me git:(master) ✗ time python build.py site Home page ............................... done Tracker list ............................ done Website list ............................ done Blog List ............................... done Blog Posts .............................. done Website pages ........................... done Tracker Pages ........................... done python build.py site 13.86s user 1.08s system 158% cpu 9.400 total ``` This will be a 5 part series dedicated to: 1. [Generating a static site (part 1)](/blog/static_site_generation.html) 2. [Visualization (part 2)](/blog/static_site_visualization.html) 3. [Building a blog (part 3)](/blog/static_site_blog.html) 4. Search, for some definition of search. 5. No third party trackers and Fast The code and data to generate this site is open-sourced at [`https://github.com/ghostery/whotracks.me`](https://github.com/ghostery/whotracks.me).

So let's start with [Generating a Static Site (part 1) ... ](/blog/static_site_templating.html) ================================================ FILE: blog/static_site_blog.md ================================================ title: Building whotracks.me - Blog (part 3) subtitle: Adding search, data, plots and blog to 1000+ pages of tracker profiles and top domains. author: privacy team type: article publish: False date: 2017-11-01 tags: blog, markdown header_img: blog/blog-site-p3.png +++ In most static site generators, one writes markdown, which is parsed and rendered into a nice blog post. So how do we do that here. As it turns out, it's pretty straight forward. We have two cases here, first the main blog page, where a list of the posts is presented, and then the post page. For both of these we have templates, which can be found at [`templates/blog.html`](https://github.com/ghostery/whotracks.me/blob/master/templates/blog.html) and [`templates/blog-page.html`](https://github.com/ghostery/whotracks.me/blob/master/templates/blog-page.html) respectively. For this, let's write a super simple function to parse the markdown file. To make its life easy, we specify a given format in the post's markdown that looks like this: ```md title: Building whotracks.me - Blog (part 3) subtitle: Adding search, data, plots and blog to 1000+ pages of tracker profiles and top domains. author: privacy team type: article publish: True date: 2017-11-01 tags: tracker-free, lightweight header_img: blog/blog-site-p3.png ➕➕➕ ``` These are all the components we need to render the snippet, and the actual blog post. As promised the parsing function is quite simple: ```python def parse(fp): ''' fp: filepath to the markdown file ''' with open(fp) as r: text = r.read() meta, body = text.split('➕➕➕') title, subtitle, author, post_type, publish, date, tags, header, _ = meta.split("\n") return { "filename": fp.split("/")[-1].replace(".md", ""), "title": title.split(":")[1].strip(), "subtitle": subtitle.split(":")[1].strip(), "author": author.split(":")[1].strip(), "type": post_type.split(":")[1].strip(), "publish": eval(publish.split(":")[1].strip()), "date": date.split(":")[1].strip(), "tags": tags.split(":")[-1].split(","), "header_img": header.split(":")[1].strip(), "body": body } ``` Alright, so now we have a way to parse the markdown to generate all parts that we need and the templates to render them, so we are left with styling. There are three elements we need to style: - the blog post card (snippet) - the actual post page. - the code snippets style Their styles are respectively defined in: - [`static/scss/blog/card.scss`](https://github.com/ghostery/whotracks.me/blob/master/static/scss/blog/card.scss) - [`static/scss/post/post.scss`](https://github.com/ghostery/whotracks.me/blob/master/static/scss/blog/post.scss) - [`static/scss/post/github.scss`](https://github.com/ghostery/whotracks.me/blob/master/static/scss/blog/github.scss) ================================================ FILE: blog/static_site_generation.md ================================================ title: Building whotracks.me - Generating a static site (part 1) subtitle: Adding search, data, plots and blog to 1000+ pages of tracker profiles and top domains. author: no one type: article publish: False date: 2017-11-02 tags: tracker-free, lightweight header_img: blog/blog-site-p1.png +++ ## 1. Generating a static site We figured that, for this problem, speed of development and richness of the ecosystem are very important, so we decided to go with Python. Since it's a new project, and we had no dependencies, we decided for [Python 3.6](https://docs.python.org/3.6/). Generating a static site boils down to two important components: * Path management * Templates This is in no way implying that all there is to a static site generator are these two, but for our needs, that was the case. Both of these are defined in [`templating.py`](https://github.com/ghostery/whotracks.me/blob/master/templating.py). ### 1.1 Path management There are two parts here. First, generating urls for shared resources (i.e. `static`, `shared data` etc), and second generating urls dynamically in the templates for all different entities (e.g.: `trackers`, `websites`, `primers` for learning etc). The first part is easy, we just store the paths of the shared resources in a dictionary, which we will later pass to the template rendering function as persistent context: ```python import os PATHS = { "_site": os.path.abspath('_site'), "static": '/static', ... } ``` For the second part, we need the `entity` we are generating a url for, and the `id` of that entity. ```python from urllib.parse import quote_plus class DataSource: #... complete class definition on github repo where # details on the structure of the data is also present @staticmethod def normalize_url(url_substring): return quote_plus(url_substring.replace("/", " ")).lower() def url_for(self, entity, id): if entity == "tracker": return "/trackers/{}.html".format(self.normalize_url(id)) elif entity == "website": return "/websites/{}.html".format(self.get_site_name(id)).lower() elif entity == "report": return "/reports/{}.html".format(id) ``` We use [quote_plus](https://docs.python.org/3.6/library/urllib.parse.html#url-quoting) here to make sure we get properly formatted urls. This is very useful to avoid errors in generating urls for entities whose id would cause issues such as `[24]7`, which is a [tracker](https://whotracks.me/trackers/24_7.html). ### 1.2. Templating The templating engine we choose was [Jinja2](http://jinja.pocoo.org/docs/2.9/). Jinja features template inheritance which is very useful for having reusable components. Although in the docs they say `python >= 3.3` support is experimental, we found it very stable. We'll be using the `Environment` object from jinja to load templates and `FileSystemLoader` as the loader [docs](http://jinja.pocoo.org/docs/2.9/api/) of choice to load templates from the file system. ```python from jinja2 import Environment, FileSystemLoader ``` Now we need a function to render the templates, and pass default jinja variables that will be shared amongst all templates: ```python import os def get_template(data, template_name): # data is an instance of DataSource env = Environment( loader=FileSystemLoader('./templates'), autoescape=select_autoescape(['html', 'xml']) ) # adding url_for as a custom flter to the environment object env.filters["url_for"] = lambda entity, id: data.url_for(entity, id) return env.get_template(template_name) def render_template(template, **context): return template.render( PATHS=PATHS, **context # template specific object(s) ) ``` The documentation on [filters in jinja](http://jinja.pocoo.org/docs/2.9/api/#writing-filters) can be found here. Note how we registered a filter in the environment. This is very useful and we use this extensively in [`templating.py#L137`](https://github.com/ghostery/whotracks.me/blob/master/templating.py#L137). ### 1.3. Building a static page In this section, we'll use the functions defined above to build a simple tracker page. ```python from templating.py import DataSource, get_template, render_template def build_tracker_page(data, tracker): # data is an instance of DataSource # tracker is a dictionary that holds # .. information on a given tracker template = get_template(data, "tracker-page.html") content = render_template( template=template, tracker=tracker ) with open('{}'.format(data.url_for('tracker', tracker["id"])), 'w') as fp: fp.write(content) return ``` All the templates are defined in [`./templates`](https://github.com/ghostery/whotracks.me/tree/master/templates). So in that folder, we must have a `tracker-page.html` template that could look like this: ```html {% extends "base.html" %} {% block content %}
{ tracker.name }}
{% endblock %} ``` We have a [`base.html`](https://github.com/ghostery/whotracks.me/blob/master/templates/base.html) where we define the shared html structure with all stylesheets and scripts. Inside the `body` in this base we declare a content block, which we will be populating in other templates (see [`base.html#L51`](https://github.com/ghostery/whotracks.me/blob/master/templates/base.html#L51)). So now in our `tracker-page.html` template we simply extend base and start populating the content block, which in our case has the tracker name with a link to its profile. Note that this is to simply show how we can access the `tracker` dictionary passed to `render_template()` and the custom filter `url_for` registered earlier in `get_template()`. The actual function for building tracker-pages is defined in [`buildsite.py#L120`](https://github.com/ghostery/whotracks.me/blob/master/buildsite.py#L120) while the template for tracker pages like [this one](/trackers/criteo.html) is defined here [`tracker-page.html`](https://github.com/ghostery/whotracks.me/blob/master/templates/tracker-page.html). ## Relevant files Do not forget to check the our [repository on github](https://github.com/ghostery/whotracks.me) for the actual implementation and more details. Relevant files are: - [`buildsite.py`](https://github.com/ghostery/whotracks.me/tree/master/buildsite.py): entry point for building pages - [`templating.py`](https://github.com/ghostery/whotracks.me/tree/master/templating.py): handles the templating and path management (discussed here) - [`templates/`](https://github.com/ghostery/whotracks.me/tree/master/templates) : Where all html templates and components are defined ================================================ FILE: blog/static_site_visualization.md ================================================ title: Building whotracks.me - Visualization (part 2) subtitle: Adding search, data, plots and blog to 1000+ pages of tracker profiles and top domains. author: privacy team type: article publish: False date: 2017-10-30 tags: tracker-free, lightweight header_img: blog/blog-site-p2.png +++ A picture says a 1000 words - or so they say. Interestingly, some recent research suggests that even when we read, our brain actually recognizes words as pictures [1]. With that said, as if one needs to justify this, having plots accompany text and numbers, is typically a good idea, and we did add some plots. # Offline plots with Plotly Choosing [Plotly](https://plot.ly/python), allowed us to keep as much of the codebase as possible in python, and have interactive plots as opposed to images. ## Plot Components The main components needed to plot something in plotly are five: `traces`, `data`, `layout`, `figure` and the `plot`object, where they're put together. - **Traces** are [`graph objects`](https://plot.ly/python/reference/) populated with the input data needed. - **Data** is a list of all traces - **Layout** is a dictionary of configuration options that determines the layout of the plot. - **Figure** is a dictionary with only two keys: `data` and `layout`, and the respective values defined earlier. - **Plot Object**: this is the plot method used (plotting online and offline) A typical function that plots something, has this rough structure: ```python def some_plot(param_0, param_1 ..., param_n): # list of traces data = [trace_0, trace_1, ..., trace_n] # Dictionary to configure the layout of the plot layout = { config_option_0: value (type:: str | int | dict) config_option_1: ____ config_option_n: ____ } # creating the fig object fig = { data = data, layout = layout } # creating the plot object (see next section for details) return plotly.offline.plot(fig, other_configurable_params) ``` We'll discuss details and provide examples on each using real examples of plots we have on this site. ## Plotting Offline This is where the plot object (referred to earlier) gets created. There are a few options. ```python from plotly.plotly import plot, iplot # create url to be viewed on plotly's # website (api key needed). With iplot # you can also open the with jupyter # notebooks from plotly.offline import plot, iplot # the first one creates a file of the # in an array of file formats, the second # creates an interactive plot without # connecting to the plotly server, but # viewable in a notebook. ``` We will be using [`plottly.offline.plot`](https://plot.ly/python/offline/) and choose `div` as the output type, which is very handy given it is html that will go into the template where it will be rendered. This enables us to generate the plots completely offline and just link the minified [`plotly.js`](https://github.com/ghostery/whotracks.me/blob/master/static/js/plotly-v1.29.3.min.js) in the head of [`base.html`](https://github.com/ghostery/whotracks.me/blob/master/templates/base.html). One downside to consider, is the 2.8MB size of plotly.js though. For us however, given the site will be served via CDN, this should be cached after the first time it loads. Let's write a function with all options we need, that will be used for all types of plots shown later in this post. This function is defined in [`plotting/utils.py`](https://github.com/ghostery/whotracks.me/blob/master/plotting/utils.py): ```python def div_output(fig, display_mode_bar=False): return plotly.offline.plot( figure_or_data=fig, output_type='div', show_link=False, include_plotlyjs=False, config={"displayModeBar": display_mode_bar} ) ``` Note that `display_mode_bar` is the set of options that shows up on the top right corner of the plot when rendered by `plotly.js`, and it looks like this:

Figure 2: Mode bar on top right corner of plotly plots.

`include_plotlyjs` is set to `False` to avoid `plotly.js` being loaded inline with the `div` output for every plot. This is not necessary as it is already linked in [`base.html`](https://github.com/ghostery/whotracks.me/blob/master/templates/base.html). ## Bar Chart On main page of this site, you will see this:

Figure 3: Horizontal bar chart on tracking reach of top 10 companies

The code to generate this can be found in [`plotting/companies`](https://github.com/ghostery/whotracks.me/blob/master/plotting/companies.py). Let's write a simpler function for a horizontal bar plot to get the idea: ```python def horizontal_bar_plot(x, y): ''' x: values y: names ''' c_purple = "#A069AB" c_gray = "#BCC4CE" trace = go.Bar( x=x, y=y, orientation='h' marker=dict( color=[c_purple]*2 + [c_gray]*8 ), ) data = [trace] layout = go.Layout( dict( showlegend=False, xaxis=dict( color=CliqzColors["gray_blue"] ) ) ) fig = dict(data=data, layout=layout) return div_output(fig) ``` ## Tracker Reach - trend Line This chart, as many others, was inspired by Edward Tufte's sparkline [2], drawn without axes or coordinates.

Figure 4: Trend line of tracker reach.

```python def sparkline(ts, t): """ Sparkline for plotting line Args: ts: timeseries data t: x-axis (time) Returns: hmtl output of an interactive timeseries plot """ y = list(map(lambda x: x * 100, ts)) # scaling percentages trace0 = line( x=t, y=y, color="#A069AB" #purple ) trace1 = line( x=[t[-1]], y=[y[-1]], color="#A069AB", mode='markers' ) layout = go.Layout( dict( showlegend=False, height=100, width=153, hoverlabel=dict( bgcolor="#1A1A25", bordercolor="#00000000", # transparent font=dict( family=WTMFonts.mono, size=13, color="#BFCBD6" ) ), xaxis=dict( autorange=True, showgrid=False, zeroline=False, showline=False, autotick=True, hoverformat="%b %y", ticks='', showticklabels=False ), yaxis=dict( # providing some padding for the sparkline range=[min(y)*0.90, max(y)*1.05 if max(y) != y[-1] else max(y)*1.15], showgrid=False, zeroline=False, showline=False, autotick=True, ticks='', showticklabels=False ) ) ) data = [trace0, trace1] fig = dict(data=data, layout=layout) return div_output(fig) ``` The code used to plot the sparkline seen in tracker profiles is defined in [`plotting/trackers.py`](https://github.com/ghostery/whotracks.me/blob/master/plotting/trackers.py). ## Sankey Diagrams Sankey diagrams are at visualizing flow volume metrics. Sometimes they are found under the name alluvial diagrams, although they originally are different types of flow diagrams.

Figure 1: Sankey diagram used to represent a [tracker map](../websites/upornia.com.html)

In this site we use sankey diagrams in website profile pages like [bahn.de](/websites/www.bahn.de.html) to map companies and the trackers they operate to the category of the tracker. The thickness of the link is a function of the frequency of of appearance of the tracker per page load in the given domain. So looking at the diagram above, we know that the dominant tracker category is advertising and Google operates the most trackers and has the highest frequency of appearance. Our Sankey Diagram function in Python looks like this: ```python from plotting.utils import div_output def sankey_plot(input_data): data_trace = dict( type='sankey', domain=dict( x=[0, 1], y=[0, 1] ), hoverinfo="none", orientation="h", node=dict( pad=10, thickness=30, label=list(map(lambda x: x.replace("_", " ").capitalize(), input_data['node']['label'])), color=input_data['node']['color'] ), link=dict( source=input_data['link']['source'], target=input_data['link']['target'], value=input_data['link']['value'], label=input_data['link']['label'], color=["#dedede" for _ in range(len(input_data['link']['source']))] ) ) layout = dict( autosize=True, font=dict( size=12 ) ) fig = dict(data=[data_trace], layout=layout) return div_output(fig) ``` Having looked at a lot of examples of sankey plots, we noticed a recurrent pattern: they do a great job at explaining the plot aesthetics, but take the structure of input data as given. This is a bit of a problem, because in most examples the input data is a huge json file, and figuring out the structure of such json file can become tedious. Here is how `input_data` is structured: ```json input_data = { "node":{ "label": [], "color": [] }, "link": { "source": [], "target": [], "value": [], "label": [], "color": [] } } ``` As you notice, input_data has two main parts: node and link: **NODE**: `input_data["node"]` is responsible for building nodes. In our example these nodes are either categories of trackers or companies that operate them. The atributes of each node are two: `label` and `color`. These are both lists of strings. These lists have to have equal length because the mapping of each label to a color is done based on the item's index in the list. **LINK**: `input_data["link"]` is responsible for linking two nodes together. Each link has the following attributes: `source`, `target`, `value`, `label` and `color`. So here is where the index of `input_data["node"]["label"]` becomes very important given the way sankey plots have been implemented in plotly. The `source` and `target` are lists of equal length, where the index is used to link.

Figure 5: Node label ilustration

The elements in `source` and `target` are in fact the indexes of the source node and target nodes in the `input_data["node"]["label"]`. So if we were to refer to the illustration in the figure above, to render our sankey diagram we would have: ```python source = [1, 1, 1, ... ] target = [0, 2, len-2, ... ] ``` With that out of the way, the remaining are intuitive: `value` represents how thick the link should be, `label` what name it has and `color` its color. All the `link` attributes are lists of equal length, and the matching is done based on index. For details, have a look at the actual implementation of the `input_data` generation in [`utils/companies.py`](https://github.com/ghostery/whotracks.me/blob/master/utils/companies.py). ## References [1] [Adding Words to the Brain's Visual Dictionary](http://www.jneurosci.org/content/35/12/4965.short)
[2] [Sparkline - Wikipedia](https://en.wikipedia.org/wiki/Sparkline)
================================================ FILE: blog/tracker-tax.md ================================================ title: Tracker Tax subtitle: The impact of third-party trackers on website speed in the United States. author: privacy team type: article publish: True date: 2018-05-29 tags: blog, update header_img: blog/tracker_tax/tracker-tax.png +++ _This post is a summary of the paper **"The Tracker Tax: the impact of third-party trackers on website speed in the United States"**. Full paper can be found [here](https://www.ghostery.com/wp-content/themes/ghostery/images/campaigns/tracker-tax/Ghostery_Study_-_The_Tracker_Tax.pdf)_.

Earlier this month, we published a study titled, _“The Tracker Tax: the impact of third-party trackers on website speed in the United States”_. The goal of this study was to shed light on the impact of trackers from a performance perspective, rather than the more frequently studied privacy standpoint. Previous research on the topic has looked at the ubiquitous nature of online tracking and their various business models[^1], pervasiveness of tracking, especially among news websites[^2] and the privacy implication of tracking in the wild where a few companies have extensive reach on web traffic [^3]. Beyond privacy concerns, we are left with one question: Do trackers cost us time? More specifically, what is the relationship between the number of trackers and the time a page takes to load? We call this tracker impact on the website page load times, also referred to as page latency, the _Tracker Tax_. ## Data Collection and Cleaning Intuition tells us that the more trackers present on a page, the longer it would take that page to load; however, this hypothesis had not been tested on a large scale. Web privacy measurement framework, [OpenWPM](https://github.com/citp/OpenWPM) has been used by numerous researchers to collect data for privacy studies on a mass, automated scale, but this tool was not built to measure website performance metrics like page load time. So we built a custom crawler to collect the number of third-party trackers on a website and the time it took that page to load. The crawler was built with Selenium running Chrome, making GET requests from a server based in New York City, and used [Ghostery](https://ghostery.com) to collect two metrics per page load: the count of third-party trackers and number of seconds to load the page. Ghostery detects third-party trackers by matching URLs from HTTP requests against their trackers' database, which currently contains over 3,000 tracker companies and 4,700 tracker patterns. To measure the time it takes a page to load, Ghostery uses Mozilla’s `Window.performance` API [^4] by taking the delta between `domContentLoadedEventStart` and `requestStart`. We chose to use Ghostery to measure tracker count and page load time so the public could replicate similar, independent analyses by simply installing the browser extension on their own and loading a website to easily see these two metrics. We ran the custom web crawler five times on each of the top 500 websites in the United States, as determined by Alexa [^5]. Data cleaning included removing domains with fewer than five successful measurements, and excluding four Chinese websites from the sample. We excluded these websites because we suspect there are China-based trackers that are not yet accounted for in the Ghostery database. Further, to account for variation in the data, we filtered out the fastest and slowest page loads per site (and their associated tracker counts), so that the data would be less sensitive to outliers and data collection errors. The crawler was run under two configurations: 1) with no trackers blocked, and 2) with the Ghostery browser extension blocking all trackers. Both configurations underwent the same data cleaning process. ## Results ### Tracker Ecosystem Using the data collected under the configuration without tracker blocking enabled, we saw that nearly 90% of page loads contained at least one tracker, 65% had at least 10 trackers, and 20% had 50 or more trackers. Only 10% of page loads were tracker free. These metrics once again confirm the prevalence of online tracking, and broadly align with our [previous study](https://www.ghostery.com/wp-content/themes/ghostery/images/campaigns/tracker-study/Ghostery_Study_-_Tracking_the_Trackers.pdf)[^6] which observed that 77.4% of page loads contain trackers. ![Tracker per page load](../static/img/blog/tracker_tax/percent-pageloads-num-trackers.svg)

Figure 1: Distribution of the number of trackers

There are several differences between our two studies which may explain the increase in tracker dominance seen in this study. Firstly, this study’s sample contains the 500 most popular websites in the US, while our previous study analyzed 144 million page loads across more than 12 countries. By only considering the most popular websites and neglecting the long tail of more obscure ones, it is not surprising that this study saw a larger proportion of sites with a tracker. Additionally, the data for this study was synthetically generated using a custom crawler, whereas our previous study used data gathered from users of the Ghostery browser extension who had opted-in to the collection of information about trackers on pages they visit. While the methodologies differ, both studies verify tracker pervasiveness throughout the web. ### Trackers and Page Latency Without blocking trackers, only 17% of all the pages in the study loaded within 5 seconds. All other pages loaded much more slowly: it took more than 10 seconds to load nearly 60% of the pages, more than 30 seconds for 18% of the pages, and nearly 5% of the pages took over a minute to load. This long tail cannot be ignored and suggests Internet users waste a lot of time every day simply waiting or websites to load. ![Time to load page](../static/img/blog/tracker_tax/avg-pageload-num-trackers.svg)

Figure 2: Average time to load trackers

While we found that websites are generally slow to load, can any of this page latency be explained by the number of third-party trackers on that site? To answer this question, we calculated the average page load time for each tracker count. We excluded both tracker volumes with fewer than five observations and page latency outliers within each tracker count (identified using the interquartile range rule). To quantify the relationship between the number of trackers on a website and the average time it took that page to load, we ran a simple linear regression (`adj-R2 0.802`) which suggested that each additional tracker adds, on average, 0.5 seconds to the overall page load. The next model we fitted, which included a quadratic term (`adj-R2 0.836`), suggests that trackers have an increasing impact on page load times. However, these linear models both exhibit heteroscedasticity – uneven variance of the error terms – and thus violate linear regression assumptions. ![Log Latency Trackers](../static/img/blog/tracker_tax/trackers-loglatency.svg)

Figure 3: Log Latency as a function of the number of trackers

A Box-Cox test showed that log-transforming the response variable would realize the best fitting model, and also act as a variance-stabilizing transformation. The log-linear model (`adj-R2 0.885`) on the transformed data indicates a compounding effect: if the tracker count increases by 1, we expect the page load time to increase by 2.5%. ### Protection from Trackers We also assessed the difference in page latency when trackers are blocked rather than allowed. The data showed that the average page load time was twice as long when trackers are not blocked: the mean page latency with no trackers blocked and with all trackers blocked was 19.3 seconds and 8.6 seconds, respectively. These time savings from blocking trackers are even more drastic when only considering the 10 slowest domains in the sample. We saw that average load times were 10x faster, and blocking trackers saved an average of 84 seconds per page load. ![Average Latency for Certain Domains](../static/img/blog/tracker_tax/avg-latency-domain.svg)

Figure 4: Latencies for certain domains

The term “piggybacking” describes the practice of one tracker that is placed directly on a website giving access to other “piggybacking” trackers that are not originally on the site. We observed this phenomenon in our data: page loads were not the only metric significantly reduced when trackers were blocked, there were also fewer trackers detected on the page. We saw significantly more trackers per page when trackers were unblocked compared to blocked, in fact, among the domains with the highest average volume of trackers, there were on average 93 fewer trackers present per page load when tracker blocking was enabled. Piggybacking can create a snowball effect, where trackers bring in more trackers that can then bring in even more trackers; and as suggested above, each additional tracker slows down a website more than previous ones. This not only has notable performance implications, but also profound privacy concerns since these trackers are not directly on the site, so site owners may not be aware such intrusion is occurring. ## Future Implications The data in our study clearly showed the pervasiveness of online tracking, as nearly 90% of the most popular sites in the US had at least one third-party tracker present. Our study also confirmed the strong, positive link between the number of trackers on a page and the time it takes that page to load. Generally, the more tracks on a site, the longer the user will have to wait for that site to load. Quantifying this relationship depends on the model used, however the optimal model we found shows a compounding effect: for every extra tracker on the page, the time it takes for the page to load increases by 2.5%. While our current study focuses on only the most popular domains in the United Sates, it would be valuable to apply this framework to other regions to see if similar trends persist elsewhere. Additionally, future work may include measuring additional performance implications of trackers including data transferred. This data transferred, which occurs when trackers make requests to other servers, bears real monetary costs to the user, particularly on a mobile device where data plans are typically based on data used. Expanding this study to assess data transfer on mobile could be translated to the out of pocket expense suffered by the user, in addition to the more subjective dollar value of the user’s wasted time waiting for pages to load. Other future work may also include looking at the relationship between bounce rates and page load speeds, to calculate a hypothetical tracker value measure. Given the additional time trackers add to page loads, and research suggest that slower pages lead to a loss in site traffic, one tracker should provide the same value as this lost site traffic. As bounce rates are likely influenced by other factors besides page load speed, like funnel page and domain category, this potential future research involves several additional considerations. Moreover, the tracker tax may even have more pronounced implications in the United States following the recent repeal of net neutrality. In a time without such net neutrality regulations, users and their browsing speeds may be squeezed from both sides – by the ISP and the online tracking ecosystem. We may then start to see more of a two prong tacker tax: the direct monetary impact imposed by the ISP and the more subjective dollar value to the user for longer load times, and therefore more unproductive time imposed by trackers. In the wake of the net neutrality repeal, now more than ever users must consider the performance implications of browsing online without protection from trackers. The added waiting times incurred by not blocking trackers are not trivial, especially as the population is spending increasingly more time online. Luckily, various tracker blocking tools are available so user can not only protect their privacy, but also speed up their browsing experience by avoiding the tracker tax. ## References [^1]: [Using Passive Measurements to Demystify Online Trackers](https://www.telematica.polito.it/users/mellia/papers/metwalleyComsi.pdf) [^2]: [WhoTracks.Me: Monitoring the online tracking landscape at scale](https://arxiv.org/abs/1804.08959) [^3]: [Tracking The Trackers](https://pdfs.semanticscholar.org/2bfb/b6b8da453f91f5860ea936588fddef6c80e0.pdf) [^4]: [Windows.performance](https://developer.mozilla.org/en-US/docs/Web/API/Window/performance) API [^5]: [alexa.com](https://alexa.com) [^6]: Ghostery Study: [Tracking the Trackers](https://www.ghostery.com/wp-content/themes/ghostery/images/campaigns/tracker-study/Ghostery_Study_-_Tracking_the_Trackers.pdf) ================================================ FILE: blog/tracker_categories.md ================================================ title: Tracker Categories subtitle: Definitions for different types of trackers author: privacy team type: primer publish: True date: 2017-07-22 tags: primer, categories header_img: blog/blog-tracker-categories.jpg +++ Trackers differ both in the technologies they use, and the purpose they serve. Based on the the service they provide to the site owner, we have categorized the trackers in the following: Advertising : Provides advertising or advertising-related services such as data collection, behavioral analysis or re-targeting. Comments : Enables comments sections for articles and product reviews Customer Interaction : Includes chat, email messaging, customer support, and other interaction tools Essential : Includes tag managers, privacy notices, and technologies that are critical to the functionality of a website Pornvertising : Delivers advertisements that generally appear on sites with adult content Site Analytics : Collects and analyzes data related to site usage and performance. Social Media : Integrates features related to social media sites Audio Video Player : Enables websites to publish, distribute, and optimize video and audio content CDN (Content Delivery Network) : Content delivery network that delivers resources for different site utilities and usually for many different customers. Misc (Miscellaneous) : This tracker does not fit in other categories. Hosting : This is a service used by the content provider or site owner Unknown : This tracker has either not been labelled yet, or we do not have enough information to label it. ================================================ FILE: blog/trackers-who-steal.md ================================================ title: The Trackers Who Steal subtitle: How WhoTracks.Me caught the trail of the MageCart hackers author: privacy team type: article publish: True date: 2018-11-23 tags: tracking, hacking header_img: blog/blog-cc-stealing.png +++ We're all aware of the trackers siphoning off information about you as you browse the web. These trackers are mostly doing this for some business intelligence related reason - websites use these services to try to 'better understand' their customers, or to target them in order to attract their attention in a way which will benefit that website owner - be-it increasing the value of products customers put into their shopping cart, or increasing the likelihood that they click an ad. However, there is another kind of tracker which is more nefarious than these. These are hidden scripts placed by hackers on E-commerce sites which try to steal your credit-card details as you enter them. In the last year a string of attacks — dubbed 'Magecart' — have affected major sites, including [British Airways](https://www.riskiq.com/blog/labs/magecart-british-airways-breach/), [Ticketmaster](https://www.riskiq.com/blog/labs/magecart-ticketmaster-breach/), [NewEgg](https://www.riskiq.com/blog/labs/magecart-newegg/) and [VisionDirect](https://twitter.com/troyhunt/status/1064069833967337472); stealing payment information from millions of consumers. At WhoTracks.Me we are monitoring the third-parties loaded on millions of pages per day, therefore once we know the domains that these hackers are using to send their stolen data, we can analyse the extent and impact of these operations. In this article we provide a post-analysis of the four big breaches this year, plus some insights our data gives in on-going attacks. ## Four high-profile breaches ### British Airways In September 2018, British Airways announced that a security breach had led to a large theft of customer data. [RiskIQ's](https://www.riskiq.com/blog/labs/magecart-british-airways-breach/) write up of the breach explains how the attackers compromised a script on the payment page, such that it would send credit card information to a domain owned by the hackers: `baways.com`. With this information we can query our data to look for page loads where `baways.com` was a third-party. This allows us to verify the extent of the breach, and how many users were affected. Our data shows that: - `www.britishairways.com` was affected between August 22nd and September 5th. 193 pages in our data were affected[^1]. - We also see two page-loads on `hotline.ba.com` on the 30th August where data was sent to the attackers. This data corroborates the [statement by BA](https://www.britishairways.com/en-gb/information/incident/data-theft/latest-information) on the breach, that users entering card details "between 22:58 BST August 21 2018 until 21:45 BST September 5 2018" would have been affected. ### Ticketmaster In June 2018, Ticketmaster declared a hack of customer information. Again, [RiskIQ's](https://www.riskiq.com/blog/labs/magecart-ticketmaster-breach/) analysis tells us how it happened - this time involving a breach of the third-party supplier Inbenta. Compromised Inbenta scripts were then loaded on ticketmaster payment pages, and these scripts then skimmed credit card data input by customers and sent it to `webfotce.me`. Unlike the British Airways case, this was not a targeted attack on Ticketmaster, rather a generic hacking program which affected many other sites. We can access the extent of these hacks by looking for the `webfotce.me` domain in our data: - Ticketmaster's UK, Irish and New Zealand sites were first affected on February 10th. The malicious script remained in place until the June 23rd, and we saw over 2,500 page loads making requests to the hackers during this time. - Their German and Australian sites appear to have been affected earlier, with our first observations on December 10th 2017 for ticketmaster.de and December 20th for ticketmaster.com.au. These sites were fixed at the same time as the others. In their [disclosure](https://security.ticketmaster.co.uk/), Ticketmaster say that UK customers were affected between February and June 23rd, and international customers could have been affected from September 2017. Again this matches up with our data, though we have no observations for international sites before December 2017. Our data also shows several other sites affected by this attack:
Site Affected from Affected to Pages
otel.com 10/12/2017 21/06/2018 125
www.cheaperthandirt.com 19/01/2018 03/06/2018 42
www.printninja.com 16/02/2018 20/11/2018 45
www.vitacost.com 26/02/2018 04/06/2018 35
thehungryjpeg.com 12/03/2018 12/06/2018 19
www.klook.com 14/03/2018 12/06/2018 28
www.steinmart.com 15/03/2018 09/07/2018 12
www.marveloptics.com 28/03/2018 22/09/2018 15

Table 1: Sites affected by webfotce.me attack.

Compared to Ticketmaster, the impact of the breach on these sites is much smaller. Correlations between the dates of infection indicate that these sites were probably infected via a shared third-party (i.e. Inbenta) which was compromised. This shows how hackers can quickly achieve much greater scale by going for third-party services whose scripts will be loaded on many different sites. ### NewEgg Like British Airways, NewEgg were hit by a [targetted attack](https://www.riskiq.com/blog/labs/magecart-newegg/). In this case the collection server was specific for the target. The hackers registered `neweggstats.com` in order to have a legitimate looking domain so that they could avoid suspicion for as long as possible. Looking at our data, we see that pages on `secure.newegg.com` were sending requests to `neweggstats.com` for just over a month, between 15th August and 18th September, with 90 pages affected in our dataset[^1]. ### VisionDirect On the November 19th, VisionDirect, a large UK-based glasses retailer, [announced](https://www.visiondirect.co.uk/customer-data-theft) that their sites had been compromised between the November 3rd and 8th. In this case, a script was injected into the page from `g-analytics.com` which pretending to be a Google Analytics script. The difference is, that it will also send credit-card numbers back when it sees them in the page. Our analysis shows that VisionDirect's European sites (`.fr`, `.it`, `.es`, `.co.uk`, `.eu` and `.nl`) were all affected from the November 3rd. On the `.nl` and `.ie` sites we still observed pages contacting the attacker's server on the 9th of November, suggesting that the malicious code may not have been completely removed as early as the press release suggests. Compared to the other collection servers, `g-analytics.com` is currently much more active with 36 sites infected during November. We have, however, observed a shift in traffic since November 20th, with almost all sites which were previously infected with `g-analytics.com` switching to loading a script from `google-analytics.is` instead. This indicates that the attacks have ongoing access to these sites, allowing them to update their attack code.
Collection Server Sites Infected
g-analytics.com 36
googletagmanager.eu 29
magento.name 19
google-analytics.is 15
trafficanalyzer.biz 5
web-stats.cc 5
bandagesplus.com 5
nearart.com 4

Table 2: Collection servers still active in November 2018

A full list of sites affected during November is available at the end of this post. ## Breach detection While WhoTracks.Me was originally conceived as a transparency tool to show trackers directly or indirectly placed by site owners, this investigation as opened up another angle on this data. We can now effectively track the spread of malicious code being used to defraud web consumers. This capability can be taken to multiple different directions: 1. Once the collection servers (or drop servers) are known, we can quickly find and notify websites that are compromised. This can reduce the exposure time of websites, and thus reduce the risk to the average web user. (Thanks to the work RiskIQ have done here to collate a list of active drop servers). 2. We can audit breaches that have occurred, and make sure websites properly notify their users. The GDPR requires that companies notify users and authorities when user data is compromised. This data can be used to hold companies accountable if they try to dodge these responsibilities. 3. Given the set of collection servers we already know, we can develop algorithms to automatically detect third-parties in pages which are similar. This would then allow us to detect and block these servers even earlier. We are very exited to start exploring this direction for our data[^2]. ## Third-party scripts: A security liability In all of these hacking cases, the entry point has been a malicious script which is loaded in the main document of the page. When this happens on a payment page, the attacker can read all of the information entered: credit card number, CVV, etc. Therefore, any script loaded on a payment page is potentially a critical security weakness. With this in mind, we should be critical of the current careless way that scripts of scattered onto what should be secure webpages. In the case of the four big breaches we have outlined here, now standard browser security features could have prevented or limited the amount of data stolen: - [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) on script tags would prevent surreptitious changes to first- and third- party scripts (provided the site's own webserver is not also compromised). If a script were to be changed to add the attacker's code the browser will refuse to load it. In the British Airways case one JavaScript file was edited to add the attack payload; for Ticketmaster the attack payload came via a third-party script. This technique provides some protection when loading content for less-trusted origins in pages which require high security. - The [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy) (CSP) header can be used to prevent requests to unknown origins. In all of these cases the credit card information was sent to a third-party collection server. A CSP header would have prevented this request, thus preventing the malicious script from exfiltrating data. As well as these high-profile cases, many of the other sites affected by these attacks are smaller E-Commerce sites using off-the-shelf software to run their business. It is therefore difficult for these sites to deploy these more advanced protection methods - even more so because the loading of 20 or 30 different untrusted third-parties on a webpage has become normalised, so users or even developers would not be able to detect unexpected third-parties appearing on a page. Related to this is another tactic employed by these hacking groups: registering domains very similar to common third-party trackers so that developers do not notice that the site is compromised. Some examples: - `g-analytics.com`, pretending to be [Google Analytics](../trackers/google_analytics.html); - `googletagmanager.eu` -> [Google Tag Manager](../trackers/google_tag_manager.html); - `slripe.com` -> [Stripe](../trackers/stripe.com.html); - `typeklt.com` -> [Adobe Typekit](../trackers/typekit_by_adobe.html); - `crtteo.com` -> [Criteo](../trackers/criteo.html); - `jsdellvr.com` -> [JSDelivr](../trackers/jsdelivr.html). ## Protecting users As we can assume that sites will continue to get hacked, we require a way we can protect users from having their data stolen without relying on site owners. This is where the browser comes in - as the user-agent it should be able to protect the user from attacks like this, much like it already does with phishing and malware sites. Luckily, as all of these attacks rely on collection servers to receive the stolen data, once we know of a server address we can use blocklists to prevent the browser from contacting these servers. Therefore, even when sites are compromised with malicious Javascript, this code will not be able to contact the hacker's server. For Cliqz and Ghostery users we have already distributed a block-list to block these domains and protect them from credit-card theft. > For Cliqz and Ghostery users we have already distributed a block-list to block these domains and protect them from credit-card theft. Blocking is just a reactive measure though. Domains are cheap, and sites are getting hacked all the time, so these hackers could easily turn over their domains faster to mitigate our blocking. Therefore, a more robust solution has to incorporate fast detection of these drop servers in order to minimise the effective lifetime of each attack. We hope to incorporate the WhoTracks.Me data in the hunt for these domains, and to emulate the speed that we are already [able to detect phishing sites](https://cliqz.com/en/whycliqz/anti-phishing). ## Conclusion In this post we've shown a new angle on the data we publish on WhoTracks.Me. As well as providing transparency on which companies are tracking you online, we are also able to turn this transparency on web criminals who are stealing from web users. This transparency can be used to: * ensure that breaches are reported promptly when they occur; * assess the impact of breaches, in terms of the timespan when sites were affected and how many users may have been affected; and * develop new techniques to catch these operations faster and reduce the number of users who suffer from them. [^1]: By "Pages Affected" we mean the number of page loads where we saw some third-party call to a server associated with MageCart operations. [^2]: Reach out to privacy@cliqz.com if you have suggestions, or would simply like to get in touch. ### Appendix: List of Magecart affected sites during November 2018
Collection Server Site Infected from infected to Number of pages
google-analytics.is www.groworganic.com 2018-11-22 2018-11-28 64
googletagmanager.eu www.wdrshop.de 2018-11-03 2018-11-28 74
google-analytics.is www.directmaterial.com 2018-11-28 2018-11-28 1
google-analytics.is www.harriscomm.com 2018-11-23 2018-11-28 9
google-analytics.is www.prospin.com.br 2018-11-24 2018-11-28 5
google-analytics.is www.electroactiva.com 2018-11-28 2018-11-28 1
magento.name www.gamesquest.co.uk 2018-11-13 2018-11-28 21
google-analytics.is www.drakegeneralstore.ca 2018-11-23 2018-11-28 23
google-analytics.is shop.tokidoki.it 2018-11-26 2018-11-28 3
magento.name store.curiousinventor.com 2018-11-01 2018-11-28 13
webfotce.me www.printninja.com 2018-11-05 2018-11-28 7
vuserjs.com www.medelita.com 2018-11-01 2018-11-27 63
googletagmanager.eu www.aneros.com 2018-11-01 2018-11-27 49
magento.name www.arrazofashion.com.br 2018-11-27 2018-11-27 1
fastproxycdn.com lessthan10pounds.com 2018-11-09 2018-11-27 19
magento.name chebdveri.ru 2018-11-02 2018-11-27 2
googletagmanager.eu www.onegreekstore.com 2018-11-02 2018-11-27 8
vmaxjs.com www.artistsnetwork.com 2018-11-01 2018-11-27 105
googletagmanager.eu slf24.pl 2018-11-08 2018-11-26 26
google-analytics.is www.pvcfittingsonline.com 2018-11-22 2018-11-26 42
googletagmanager.eu www.bestkiteboarding.com 2018-11-09 2018-11-26 24
qsxjs.com vapenw.com 2018-11-02 2018-11-26 86
magento.name www.compremake.com.br 2018-11-20 2018-11-26 3
valdatecode.com www.carnivalbkk.com 2018-11-03 2018-11-26 52
googletagmanager.eu www.mobileparadise.de 2018-11-21 2018-11-26 4
googletagmanager.eu www.cht-cottbus.de 2018-11-03 2018-11-26 79
privatejs.com www.bydubai.com 2018-11-01 2018-11-26 55
google-analytics.is www.scojo.com 2018-11-24 2018-11-26 10
googletagmanager.eu www.nordhandel.de 2018-11-01 2018-11-25 106
alfcdn.com www.softstarshoes.com 2018-11-25 2018-11-25 2
magento.name www.prestigioplaza.com 2018-11-25 2018-11-25 3
googletagmanager.eu www.wslstore.com 2018-11-18 2018-11-25 2
magento.name www.herve-leger-shop.com 2018-11-25 2018-11-25 1
googletagmanager.eu amsducati.com 2018-11-05 2018-11-25 6
google-analytics.is www.ozarksource.com 2018-11-24 2018-11-24 1
g-analytics.com geissele.com 2018-11-10 2018-11-24 85
crtteo.com www.accessorygeeks.com 2018-11-01 2018-11-23 31
google-analytics.is drdennisgross.com 2018-11-22 2018-11-23 4
googletagmanager.eu www.everbestshoes.com 2018-11-23 2018-11-23 1
googletagmanager.eu unitedsalonsupplies.com 2018-11-23 2018-11-23 2
trafficanalyzer.biz www.oaknyc.com 2018-11-19 2018-11-23 2
googletagmanager.eu dampoteket.no 2018-11-07 2018-11-23 10
magento.name www.ikonmotorsports.com 2018-11-05 2018-11-23 8
google-analytics.is www.dreamduffel.com 2018-11-23 2018-11-23 4
nearart.com www.westcottbrand.com 2018-11-04 2018-11-23 13
magento.name oramaoptics.gr 2018-11-21 2018-11-23 3
google-analytics.is www.cruyffclassics.com 2018-11-23 2018-11-23 3
magento.name www.weldingsuppliesdirect.co.uk 2018-11-05 2018-11-22 9
googletagmanager.eu hk.ap-nutrition.com 2018-11-13 2018-11-22 3
nearart.com www.camillusknives.com 2018-11-03 2018-11-22 27
google-analytics.is www.softballfans.com 2018-11-22 2018-11-22 3
magento.name www.ammerer.com 2018-11-02 2018-11-22 4
g-analytics.com www.candent.ca 2018-11-22 2018-11-22 1
googletagmanager.eu www.autosiliconehoses.com 2018-11-03 2018-11-21 29
google-analytics.is temptu.com 2018-11-21 2018-11-21 4
googletagmanager.eu www.lampen-line.de 2018-11-04 2018-11-21 16
googletagmanager.eu www.airagestore.com 2018-11-05 2018-11-21 6
g-analytics.com drdennisgross.com 2018-11-11 2018-11-20 8
g-analytics.com pvcpipesupplies.com 2018-11-12 2018-11-20 5
g-analytics.com www.cruyffclassics.com 2018-11-08 2018-11-20 13
g-analytics.com www.pvcfittingsonline.com 2018-11-08 2018-11-20 77
g-analytics.com www.ahmadtea.com 2018-11-09 2018-11-20 10
g-analytics.com www.groworganic.com 2018-11-04 2018-11-20 87
web-stats.cc www.kingfishertapes.co.uk 2018-11-20 2018-11-20 3
g-analytics.com www.fabglassandmirror.com 2018-11-10 2018-11-20 9
statsdot.eu www.punkstuff.com 2018-11-20 2018-11-20 14
onefromeu.com www.joyfolie.com 2018-11-03 2018-11-20 16
listrakb.com www.skistart.com 2018-11-02 2018-11-19 4
g-analytics.com www.energymuse.com 2018-11-06 2018-11-19 72
googletagmanager.eu www.casinhabonita.com.br 2018-11-06 2018-11-19 20
g-analytics.com www.frightprops.com 2018-11-15 2018-11-19 3
statsdot.eu storeinfinity.com 2018-11-07 2018-11-19 10
g-analytics.com www.especialneeds.com 2018-11-12 2018-11-19 21
g-analytics.com www.stmgoods.com.au 2018-11-09 2018-11-18 7
onefromeu.com www.poshshop.com 2018-11-13 2018-11-18 39
googletagmanager.eu deanzelinsky.com 2018-11-07 2018-11-18 11
googletagmanager.eu nativetreasuresnm.com 2018-11-10 2018-11-18 8
g-analytics.com vapage.com 2018-11-13 2018-11-18 23
magento.name www.hydraulicsonline.co.uk 2018-11-02 2018-11-18 2
nearart.com mitchellssalon.com 2018-11-18 2018-11-18 1
g-analytics.com altheatsupply.com 2018-11-14 2018-11-18 5
scriptsfyou.com adamspolishes.com 2018-11-01 2018-11-17 55
googletagmanager.eu www.recifeingressos.com 2018-11-16 2018-11-17 3
g-analytics.com www.stmgoods.com 2018-11-10 2018-11-16 12
g-analytics.com temptu.com 2018-11-06 2018-11-16 7
g-analytics.com www.drakegeneralstore.ca 2018-11-16 2018-11-16 1
g-analytics.com shop.tokidoki.it 2018-11-15 2018-11-15 3
g-analytics.com medmartonline.com 2018-11-13 2018-11-15 4
g-analytics.com intl.drdennisgross.com 2018-11-15 2018-11-15 2
googletagmanager.eu ikiegeszitok.hu 2018-11-08 2018-11-15 11
g-analytics.com www.weareverincontinence.com 2018-11-12 2018-11-14 3
cdnscriptx.com www.cartouchesarabais.com 2018-11-11 2018-11-14 14
g-analytics.com cig2o.com 2018-11-14 2018-11-14 1
fastproxycdn.com tilebar.com 2018-11-03 2018-11-14 120
g-analytics.com www.curediva.com 2018-11-07 2018-11-13 6
typeklt.com www.mariatash.com 2018-11-02 2018-11-13 49
g-analytics.com www.lucerooliveoil.com 2018-11-13 2018-11-13 5
g-analytics.com www.plumbingsupplynow.com 2018-11-13 2018-11-13 1
magento.name www.grafipronto.pt 2018-11-12 2018-11-12 1
checkercarts.com www.shambhala.com 2018-11-01 2018-11-12 19
scriptsenvoir.com www.heatpressnation.com 2018-11-01 2018-11-12 48
typeklt.com www.cabletiesunlimited.com 2018-11-09 2018-11-12 6
web-stats.cc www.costway.de 2018-11-07 2018-11-10 2
g-analytics.com www.visiondirect.ie 2018-11-05 2018-11-09 4
web-stats.cc www.rincondidactico.cl 2018-11-09 2018-11-09 1
g-analytics.com www.visiondirect.nl 2018-11-04 2018-11-09 41
magento.name patbo.com.br 2018-11-05 2018-11-09 3
googletagmanager.eu professional.imageskincare.nl 2018-11-09 2018-11-09 2
googletagmanager.eu consument.imageskincare.nl 2018-11-09 2018-11-09 2
magento.name eaccesoriigsm.ro 2018-11-08 2018-11-08 1
jspoi.com www.padini.com 2018-11-04 2018-11-08 3
g-analytics.com www.visiondirect.co.uk 2018-11-03 2018-11-08 112
googletagmanager.eu www.oddbins.com 2018-11-01 2018-11-08 9
g-analytics.com www.visiondirect.fr 2018-11-03 2018-11-07 53
magento.name upmarketpets.com 2018-11-07 2018-11-07 1
g-analytics.com www.visiondirect.it 2018-11-04 2018-11-07 2
g-analytics.com www.visiondirect.es 2018-11-05 2018-11-07 26
upgradenstore.com www.armysurplusworld.com 2018-11-06 2018-11-06 1
g-analytics.com www.ozarksource.com 2018-11-06 2018-11-06 1
upgradenstore.com www.princesspolly.com 2018-11-01 2018-11-06 3
locatefyou.com www.jjroofingsupplies.co.uk 2018-11-01 2018-11-06 10
g-analytics.com www.prospin.com.br 2018-11-06 2018-11-06 1
web-stats.cc www.baleyo.com 2018-11-06 2018-11-06 1
maxijs.com copperlab.com 2018-11-05 2018-11-05 9
gamacdn.com csvape.com 2018-11-03 2018-11-05 2
valdatecode.com www.pfiwestern.com 2018-11-01 2018-11-05 15
googletagmanager.eu erecycleronline.com 2018-11-05 2018-11-05 1
magento.name nicoman.co.uk 2018-11-01 2018-11-05 2
minifyscripts.com shop.bombingscience.com 2018-11-03 2018-11-04 4
web-stats.cc shelfadditions.com 2018-11-04 2018-11-04 2
jspoi.com store.asqgrp.com 2018-11-01 2018-11-04 3
trafficanalyzer.biz www.irishnewsarchive.com 2018-11-03 2018-11-03 1
magento.name www.cochesdemetal.es 2018-11-01 2018-11-03 2
magento.name originalnye-zapchasti.com 2018-11-02 2018-11-02 1
googletagmanager.eu www.exeltek.com.au 2018-11-02 2018-11-02 2
g-analytics.com www.hyperparapharmacie.com 2018-11-02 2018-11-02 1
amasty.biz www.decantshop.com 2018-11-01 2018-11-01 1
jspoi.com massivejoes.com 2018-11-01 2018-11-01 4
cdnrfv.com www.versare.com 2018-11-01 2018-11-01 18
magento.name www.yourdezire.co.uk 2018-11-01 2018-11-01 2
allacarts.com www.plumprettysugar.com 2018-11-01 2018-11-01 6
================================================ FILE: blog/trackers_in_your_favorite_site.md ================================================ title: Making sense of the trackers on Reddit subtitle: Using whotracks.me data and sankey diagrams to dissect trackers author: privacy team type: article publish: True date: 2018-01-08 tags: trackers, sankey header_img: blog/trackers_on_site/blog-sankey-1.png +++ In this post we'll try to do two things: 1. teach you how to easily build sankey diagrams. 2. show you how to use whotracks.me data and API to investigate trackers on reddit, or any of your favourite sites, using sankey diagrams ## Sankey Diagrams When building the tracker maps that you see on popular site profiles on whotracks.me, sankey diagrams seemed like a good fit to map categories of tracking to companies that own the trackers. Each link would be a tracker, going from a category to a company. Trackers on Tumblr

Figure 1: Sankey diagram used to represent a [tracker map](../websites/tumblr.com.html)

Sankey diagrams are great at visualizing flow volume metrics. Sometimes they are found under the name alluvial diagrams, although they originally are different types of flow diagrams [1]. We wanted to use the sankey diagram supported in plotly [2], the visualisation library of choice used in whotracks.me. The function itself is pretty simple, as you will see in a bit when we define `sankey_diagram()`. The challenge to creating sankey diagrams with Plotly is understanding the required structure of the input data required by the plotting function. Hopefully the following example will make it easier for those reading this post, should they ever decide to try sankey diagrams. The goal here is to show a very small dataset, structured in a way that the plotly diagram (and other plotting solutions e.g.: d3.js) understand. We will be mapping cities to the countries they are part of. The value of each link, will be the city population (in millions). ```python city_data = dict( nodes = dict( label=["Germany", "Berlin", "Munich", "Cologne", "France", "Paris", "Lyon", "Bordeaux"], color=["beige", "black", "red", "yellow", "beige", "blue", "white", "red"] ), links = dict( source=[0, 0, 0, 4, 4, 4], target=[1, 2, 3, 5, 6, 7], value= [3.5, 1.5, 1, 2.2, 0.5, 0.2], label=["capital", "city", "city", "capital", "city", "city"], color=["black", "red", "yellow", "blue", "whitesmoke", "red"] ) ) ``` Note how there are two keys in the dictionary, `nodes` and `links`, and each has some attributes. Let's go over them. Each node has a label (e.g. Germany) and a corresponding `color` (in this case `beige`). Note that labels and colors are stored in lists of equal length, and the pairing is done based on equality of that index. Links contain information about how to link nodes. Each has a `source`, `target`, `value`, `label` and `color`. Source contains the index in the list of the source node, whereas target the index in the list of the target node. Value determines how thick the link should be (in our case it will be the population of each link, hence each city), Label and color, as the name suggests, specify the label and color of the link. Links too, are paired based on index. ## Plotting a sankey diagram Now let's write a simple function to plot this data nicely. Most of the work has already been done, given we're feeding the data in a format that's easy to parse. ```python from plotly.offline import iplot def sankey_diagram(sndata, title): # First part of a plotly plot is the `trace` data_trace = dict( type='sankey', node=dict( pad=10, thickness=30, # label could easily be equal to sndatap['node]['label']. The following is just cosmetics label=list(map(lambda x: x.replace("_", " ").capitalize(), sndata['nodes']['label'])), color=sndata['nodes']['color'] ), link=sndata["links"], # configuration options for the diagram domain=dict( x=[0, 1], y=[0, 1] ), hoverinfo="none", orientation="h" ) # Second part of a plotly plot is the `layout` layout = dict( title=title, font=dict( size=12 ) ) fig = dict(data=[data_trace], layout=layout) return iplot(fig) ``` ## Sankey diagram for a few German and French cities All that is left now, is feeding the `city_data` to the `sankey_diagram` function and we're done. Simple sankey diagram

Figure 1: Simple example of a sankey digram for cities

Trying to create the flags of these countries did not end up being such an aesthetically good idea. # From Cities to Trackers Doing Sankey diagrams for cities may have been fun. The result of doing the same for trackers on your favourite sites might not be as fun -it may in fact be terrifying. We'll be using public data from whotracks.me to map tracker categories to companies present on a particular site. Each link will be a tracker the company owns. This gives immediate visual insights on who's watching you and why. ## Terse intro to the API The data and API for whotracksme us available on Pypi and you can easily install it running `pip install whotracksme`. ```python from whotracksme.data.loader import DataSource from whotracksme.website.plotting.colors import tracker_categoryColors, cliqz_colors ``` DataSource is a class that provides access to trackers, companies that own them, and popular websites. The functionality of DataSource is something we'll be constantly trying to improve and expand. Online tracking is messy enough to analyze, so at least the tooling should be as simple as possible. We will be looking at Reddit. If you are not familiar with Reddit, check it out - there are some great communities there. Now we'll look at the tracking landscape in reddit. To do that, we only need to know the reddit `site_id`, which is `reddit.com`. Each site has a `site_id`, most often its url. ## Preparing reddit tracker data for sankey diagram Here we will be mapping companies and the trackers they operate to the category of the tracker. The thickness of the link is a function of the frequency of appearance of the tracker per page load in the given domain. ```python def sankey_data(site_id, data_source): nodes = [] link_source = [] link_target = [] link_value = [] link_label = [] for (tracker, category, company) in data_source.sites.trackers_on_site(site_id, data_source.trackers, data_source.companies): # index of this category in nodes if category in nodes: cat_idx = nodes.index(category) else: nodes.append(category) cat_idx = len(nodes) - 1 # index of this company in nodes if company in nodes: com_idx = nodes.index(company) else: nodes.append(company) com_idx = len(nodes) - 1 link_source.append(cat_idx) link_target.append(com_idx) link_label.append(tracker["name"]) link_value.append(100.0 * tracker["frequency"]) label_colors = [tracker_categoryColors[l] if l in tracker_category_colors else cliqz_colors["purple"] for l in nodes] return dict( nodes = dict( label=nodes, color=label_colors ), links = dict( source=link_source, target=link_target, value=link_value, label=link_label, color=["#dedede"] * len(link_label) ) ) ``` Now that we have a function to generate the data in the format we need it, let's run it for reddit and plot the sankey diagram to investigate the tracking landscape: ```python input_data = sankey_data('reddit.com', data_source=DataSource()) sankey_diagram(input_data, 'Tracker Map on reddit.com') ``` Reddit Tracking Landscape

Figure 1: Tracking landscape on reddit.com

We see that most tracking happens for advertising reasons. Although it does not seem like it, Reddit is keeping the set of advertisers they expose their users somwhat limited compared to other portals and news sites. In terms of number of trackers, Google has the most eyes on reddit users. For more details on the tracking landscape on reddit, head over to reddit's [profile page](https://whotracks.me/websites/reddit.com.html) on whotracks.me. ## References [[1] Sankey Diagrams](https://en.wikipedia.org/wiki/Sankey_diagram) - Wikipedia
[[2] Plotly - Python Graphing Library](https://plot.ly/python/)
[[3] Jupyter Notebook on this post](https://nbviewer.jupyter.org/github/ghostery/whotracks.me/blob/master/contrib/tracker_map_notebook.ipynb) ================================================ FILE: blog/tracking_and_ux.md ================================================ title: The Impact of Tracking on User Experience subtitle: Beyond privacy - a survey of hidden and visible effects of tracking on user experience. author: privacy team type: article publish: True date: 2017-11-07 tags: tracker-free, lightweight header_img: blog/tracking_and_ux/ux_header.png redirect_url: https://www.ghostery.com/blog/tracking-and-ux +++ We all have been in situations in which we felt that the Internet knows us too well: When the pair of shoes that we looked for suddenly starts to haunt us on almost every single web page. The “magic” behind this phenomenon is called tracking - a controversial topic, for security and privacy reasons. However, tracking is not only relevant from a privacy perspective, it also affects user experience in many ways. While some of the effects are visible and obvious to everyone, others are hidden behind the scenes. ## A Positive Side to Tracking? But before we get started with the actual effects, let’s quickly clarify one thing: Trackers are not good or bad per se. In fact, trackers are often just a by-product of innovative technologies that were invented to make the user’s life easier in the first place. For example, content delivery networks (CDN) assure a speedy delivery of web pages, analytics tools help to better understand users to improve page structures, hosted fonts allow designers to ensure that fonts look as intended, and single sign-ons increase convenience while mitigating password fatigue. It's the way these technologies are implemented and used that may or may not introduce privacy and user experience side-effects. ## The Visible Effects of Tracking Online ads are a good example of the visible effects of tracking. While some users certainly appreciate ads, many find them annoying or creepy. This is also reflected by the increasing usage of ad blockers, which grew by 30% in 2016 alone [1]. ### Distraction One reason for this annoyance is that ads fight for the users’ attention—in particular ads that use aggressive visuals or animations (Figure 1). They distract users from the actual page content and the task at hand. This seems to contradict "banner blindness", the phenomenon of having learnt to ignore ads. But rather than ignoring ads, users seem to ignore the content instead, while banners still cause distraction [7].

Figure 1: banner ads are intrusive and distracting. In this example they are placed right next to the content to get the users’ attention.

### Deception Another reason for annoyance is the use of native advertising: ads that are designed to resemble content as much as possible (Figure 2). The main goal is to maximize click-through rates on ads by deliberately misleading users on the nature of the content. Although users are less likely to notice the presence of native advertising compared to traditional banner ads [9], it is more difficult for them to distinguish organic content from paid ads.

Figure 2: native advertising is deceptive as it makes it difficult for users to distinguish organic content from paid ads.

### Page Breakage Last but not least, ads and tracking increase page loading times: users have to wait substantially longer for content to appear, which degrades the online user experience. The average data usage by trackers amounts to more than 6MB per page load [2]. In a Mozilla study, researchers further found that the average number of reported problems with web pages was higher for users with tracker blocking disabled, relative to those with it enabled.[3]. These users reported more often that web pages felt slow, laggy, or unresponsive. This is surprising because tracking protection is often the reason for such page breakage. ### Moments of embarrassment The facets discussed so far all relate to functional problems (i.e. web pages do not work, users cannot complete their tasks, etc.). However, there is also another dimension to the visual effects of tracking: social implications. In order to deliver the most relevant ads to the user, they are often targeted and based on previous online behaviors, such as page visits. For example, a user would see ads for sports shoes on a news page after having searched for them on a shopping site. While the majority of users are opposed to behavioral targeting and are concerned about their privacy [4, 10], behavioral targeting can affect the user experience in a much more direct way, in particular when sharing a computer: Imagine an online purchase for your loved one popping up on a web page visited by the future gift recipient — surprise ruined. Or imagine a friend looking over your shoulder and getting a glimpse on an ad about something that you find embarrassing. ## The Hidden Effects of Tracking Yet, a large part of tracking takes place behind the scenes of the shiny web surface. It's not obvious that users are being observed, yet trackers record all their page visits [6]. This is not only a privacy problem, it also affects the user experience. ### Lack of Transparency Most users are aware that their searches and interactions are recorded when using services like Facebook or Amazon. After all, they are explicitly registered and logged in. Users understand that such services need to know certain things to provide their services, for example, to show interesting posts or to suggest new friends. However, a large part of tracking takes place via third-party trackers, scripts that are embedded on pages around the web or are part of a browser add-on without the users' awareness. These scripts call home to report on each user's behavior—often without having asked for permission. It is not transparent to users that their oftentimes personal data is shared, with whom it is shared, and where it is stored. For example, users were surprised to learn that browsing history is used to target ads [10]. ### Lack of Control Even if users knew about the extent of tracking taking place, there is still a lack of control. Once the data is out on some servers, users do not have the option to audit or delete the data stored about them. Current approaches for giving control to users are not understood by users [10]. ### Transparency and Control are Critical Why are transparency and control so important? Data collected by trackers reveal more about a person than you might think. One page visit may not tell who you are, but the visit of multiple pages does. Trackers connect these visits through unique identifiers. Suddenly the virtual self turns into a real person: Profile pictures from social networks reveal the visual appearance, location sharing exposes home and workplace, and shopping behavior hint at personal preferences. All this happens without the awareness of the user—the user experience on the surface does not reveal the operating network of trackers underneath it. ## The UX Challenges Both visible and hidden effects of trackers on user experience are non-trivial to address. Numerous applications or add-ons exist to remove ads from web pages or to reduce the effects of tracking. Adblock Plus and NoScript are two popular examples.

Figure 3: an example of an ad block wall encouraging users to whitelist trackers.

However, removing ads leads, similar to ads in the first place, to page breakage. News sites, for example, use adblock detection to put up ad block walls, asking users to whitelist ads in order to access the content (Figure 3). Ad block walls not only degrade the user experience but also reduce traffic to the underlying pages: a recent survey found that 74% of American adblock users choose to leave sites with adblock walls [1]. Another example are pages without visible ads, but that use scripts for tracking. Blocking all scripts offers protection, but makes modern web pages unusable as many features rely on scripting. Overall, tracking is a complex topic. Its technical foundation is hard to grasp for most users. Users build their mental models about how trackers work based on their own experiences. This leads to wrong beliefs, such as that Facebook cannot track users once they are logged out of the platform. On the other hand, advertising is often the only revenue stream for web sites. Users benefit from it as websites can run without charging their users. Nonetheless, revenue should never come at the cost of the users’ privacy. It is not easy, but targeted advertising does not have to rely on tracking [5]. Users should always be in control over their data. This is the paradigm that Cliqz follows in their products [11]. The challenges, from a user experience point of view, lie in educating users in a simple enough way so that they understand the effects of tracking and in allowing users to decide which data they want to share or not to share. We love to hear your thoughts on this topic. ##References [1] [2017 Adblock Report](https://pagefair.com/blog/2017/adblockreport/)
[2] [Trackers Rank](/trackers.html)
[3] [Privacy Settings Breakage Study](https://docs.google.com/presentation/d/1OVtXAnyeBLX2N1yyZoTMP9AV_6HnI3mnXwIFlOL7yOA/edit)
[4] [Americans Reject Tailored Advertising and Three Activities That Enable It](http://repository.upenn.edu/cgi/viewcontent.cgi?article=1138&context=asc_papers)
[5] [Adnostic: Privacy Preserving Targeted Advertising](http://www.nyu.edu/pages/projects/nissenbaum/papers/adnostic.pdf)
[6] [Tracking the Trackers](https://static.cliqz.com/wp-content/uploads/2016/07/Cliqz-Studie-Tracking-the-Trackers.pdf)
[7] [Banner ads hinder visual search and are forgotten](https://dl.acm.org/citation.cfm?id=986008)
[8] [A Review of Online Advertising Effects on the User Experience](https://pdfs.semanticscholar.org/54b4/c030742848f26bd69910d678da6a713a9d5e.pdf)
[9] [Native Advertising and Digital Natives: The Effects of Age and Advertisement Format on News Website Credibility Judgments](http://www.academia.edu/download/37165600/ISOJ_Journal_V4_N1_2014_Spring.pdf#page=79)
[10] [Smart, Useful, Scary, Creepy: Perceptions of Online Behavioral Advertising](https://pdfs.semanticscholar.org/db4c/502bafef23e7d8b1de60d628c952a4780acc.pdf)
[11] [MyOffrz](https://myoffrz.com/fuer-nutzer/) ================================================ FILE: blog/tracking_pixel.md ================================================ title: Tracking Pixel subtitle: So, ... did you read my email? author: privacy team type: primer publish: True date: 2017-07-22 tags: primer, tracking header_img: blog/blog-pixel.jpg +++ A tracking pixel, is one of various techniques used on web pages or email, to unobtrusively (usually invisibly) allow checking that a user has accessed some content. Common uses are email tracking and page tagging for web analytics. Alternative names are `web beacon`, `web bug`, `tracking bug`, `tag`, or `page tag`, `pixel tag`, `1×1 gif`, and `clear gif`. When implemented using JavaScript, they may be called JavaScript tags [[1](https://w2.eff.org/Privacy/Marketing/web_bug.html)] There is a work in progress to standardize an interface that web developers can use to asynchronously transfer small HTTP data from the User Agent to a web server that call it simply beacons (in the context of web development) which can be used to send data to a web server prior to the loading of the document without delaying the load and affecting the perception of page load performance for the next navigation [[2](http://www.w3.org/TR/beacon/)]. The excerpt above has been retrieved from [wikipedia](https://en.wikipedia.org/wiki/Web_beacon). ## References [1] [The Web Bug FAQ](https://w2.eff.org/Privacy/Marketing/web_bug.html)
[2] [Beacon](http://www.w3.org/TR/beacon/)
[3] Source: [Wikipedia](https://en.wikipedia.org/wiki/Web_beacon) ================================================ FILE: blog/update_apr_2018.md ================================================ title: April Update - Preparing for Internationalisation subtitle: A new data format to ease access to tracker data. author: privacy team type: article publish: True date: 2018-04-12 tags: blog, update header_img: blog/blog-data-apr18.png +++ _This post is one of our regular monthly blogs accompanying an update to the data displayed on WhoTracks.Me. In these posts we introduce what data has been added as well as point out interesting trends and case-studies we found in the last month. Previous month's posts can be found here: [February 2018](./update_feb_2018.html), [January 2018](./update_jan_2018.html), [December 2017](./update_dec_2017.html)._ This month we have a big update to the site. We have restructured the data we publish to make it easier to use, increased the number of entries we publish, and we have laid the groundwork for internationalised versions of WhoTracks.Me - that means you can see how tracking differs between different countries. Thanks to integration with Ghostery 8 we collected significantly more tracker data this month, covering 360 million page loads. This is spread over countries across the world, with Germany and the USA the most represented. ![Page loads per country, March 2018](../static/img/blog/update_apr18/page_loads_per_country.svg)

Figure 1: Page loads per country, March 2018

This volume of data will also enable us to publish WhoTracks.Me content for individual countries, something we plan to add later this month. ## Data restructuring We have updated the structure of data which we publish in our [repository](https://github.com/ghostery/whotracks.me/) to make it both easier to use and more scalable as we add more data. We now publish CSV files each month for each of the following: * `domains.csv`: Top third-party domains seen tracking. * `trackers.csv`: Top trackers - this combines domains known be operated by the same tracker. * `companies.csv`: Top companies - aggregates the stats for trackers owned by the same company. * `sites.csv`: Stats for number of trackers seen on popular websites. * `site_trackers.csv`: Stats for each tracker on each site. These files can then be loaded with popular data-analysis tools such as [Pandas](https://pandas.pydata.org/). We have also rewritten the code to render the site to take advantage of Pandas. We expose the dataframes via the `DataSource` class which loads data from all CSV files: ```python from whotracksme.data.loader import DataSource data = DataSource() len(data.trackers.df) >> 7928 ``` We have also updated the criteria by which we include trackers and sites on the main site. We now 'rollover' entries, so once they have been included once, we will keep publishing data (until they completely disappear from the data). This has the effect of naturally growing the number of trackers and sites we publish. We currently have data on 868 trackers and 748 websites published: ```python pd.DataFrame({ 'trackers': data.trackers.df.groupby('month').count()['tracker'], 'sites': data.sites.df.groupby('month').count()['site'] }).plot() ``` ![Growth of trackers and sites](../static/img/blog/update_apr18/data_growth.svg)

Figure 2: Growth of trackers and sites

The per-site trend for average number of trackers continues a slightly downward trend, although the average is still high at 9 trackers per page. There are several possible reasons for this, it is not necessarily that sites are using fewer trackers! The proportion of data from Ghostery users continues to increase, and these users will disproportionately block many trackers. This has an effect on the average number of trackers, because it prevents the blocked trackers from loading others. The data shows also that the average incidence of blocking for trackers increased to 25% in March, up from 20% in February. ```python sns.boxplot( data=data.sites.df[data.sites.df.month >= '2018-01'], x='month', y='trackers' ) ``` ![Average trackers per page since January](../static/img/blog/update_apr18/site_trackers_box.svg)

Figure 3: Average trackers per page since January

```python (data.trackers.df[data.trackers.df.month >= '2018-01'] .groupby('month') ['has_blocking'].mean() * 100).plot() ``` ![Blocking Trend since January](../static/img/blog/update_apr18/blocking_trend.svg)

Figure 4: Blocking Trend since January

As in previous months, we look at sites' changing their trackers. [fewo-direct.de](../websites/fewo-direkt.de.html), [brigitte.de](../websites/brigitte.de.html) and [gutefrage.net](../websites/gutefrage.net.html) all had 5 fewer trackers on average per page this month. However, each of these still has over 50 trackers with some kind of presence, showing that this is more likely a side-effect of increased blocking than an active effort to reduce tracking on their sites. [klingel.de](../websites/klingel.de.html) and [informationvine.com](../websites/informationvine.com.html) see the largest increase in tracking of the sites we currently monitor.
Site Trackers Change since February
informationvine.com 18.3 +6.4
klingel.de 26.7 +5.3
gutefrage.net 13.0 -5.6
brigitte.de 19.5 -5.8
fewo-direkt.de 16.0 -6.6

Table 1: Websites Tracking Trends

A side-effect of the filtering we added in this new data pipeline is that the site reach for top trackers has increased. In the previous analysis a long-tail of very rarely visited sites reduced effective site reach. With this factor reduced, we get a real sense of the coverage of the largest trackers, with Google Analytics reaching 85% of popular sites, and Facebook almost 60%. The data can easily be retrieved as shown below: ```python df = data.trackers.get_snapshot().sort_values(by='site_reach', ascending=False).head(10) df['name'] = df.id.apply(func=lambda x: data.app_info[x]['name']) ``` ![Reach of top 10 trackers across popular websites](../static/img/blog/update_apr18/top10_site_reach.svg)

Figure 5: Reach of top 10 trackers across popular websites

If you want to delve deeper into our data, it is available on the [WhoTracks.Me Github Repository](https://github.com/ghostery/whotracks.me/tree/master/whotracksme/data), and as a [pip package](https://pypi.python.org/pypi/whotracksme/). _NB: The code snippets here will not generate the presented plots. Full code snippets for the plots in this post are available in this [Jupyter Notebook](https://nbviewer.jupyter.org/github/ghostery/whotracks.me/blob/master/contrib/wtm_april_update.ipynb)._ ================================================ FILE: blog/update_dec_2017.md ================================================ title: WhoTracks.me December Update subtitle: New data and trackers in our monthly update. author: privacy team type: article publish: True date: 2017-12-08 tags: blog, update header_img: blog/blog-data-dec17.png +++ We're happy to update the site today with data from November 2017 - based on data from 100 million page loads. We're also expanding the amount of data we show, up to 600 top websites and 600 top trackers. ## New Trackers in the database Increasing the number of trackers displayed meant that we needed to add tracker information for a new batch of tracker domains, as well as new entrants appearing in the top 500. Here are the 3 most interesting entrants: * [Tru Optik](../trackers/truoptik.html), a company offering targeted advertising for Smart TVs, and claiming 70 Million US households in their 'Household Graph'. Their presence across major German sites suggests they might be using online ad networks in order to harvest user information and link it to active Smart TVs, where they can then push targeted adverts. * [Digitrust](../trackers/digitrust.html), a non-profit aiming to reducing the number of third-party requests per page. Their solution, however, is to create a unified user identifier, intended to prevent the need for trackers to synchronise pixels and tracking tokens on each page. Notably, they state that they [do not support](http://www.digitru.st/faqs/) the [Do Not Track](https://en.wikipedia.org/wiki/Do_Not_Track) standard, so their claims to be working in consumers interests are, at best, suspect. * [ORC International](../trackers/orc_international.html), the registered owner of the domain `emxdgt.com`, and a subsiduary of [Engine](http://www.enginegroup.com/), an Advertising Agency. Despite only appearing in our data recently, they have quickly risen up to the top 300 trackers, and are listed in the [ads.txt](https://iabtechlab.com/ads-txt/) files as a reseller for several major US publications, such as [The Atlantic](https://www.theatlantic.com/ads.txt) and [CNET](https://www.cnet.com/ads.txt). Their ownership, and policy for the data collection is, however, not transparently disclosed. ## Month-to-month trends The average number of trackers on top websites increased to 10, an increase of 3%. [Heine.de](../websites/heine.de.html), [gutefrage.net](../websites/gutefrage.net.html), [sportscheck.com](../websites/sportscheck.com.html) and [bild.de](../websites/bild.de.html) increased their number of trackers the most, each of them added on average 5 more trackers page page load. At the other end of the spectrum, [paket.de](../websites/paket.de.html), [jackpot.de](../websites/jackpot.de.htmwl) and [hurriyet.com.tr](../websites/hurriyet.com.tr.html) had on average 5 fewer trackers per page. On the tracker side, the biggest gain was by [pmddby.com](../trackers/pmddby.com.html), which increased its reach by 9 times since October. Its profile is that of Spyware which is injecting ads into webpages for affected users, however at this time we were not able to determine the source - the WHOIS data for the domain is private. ## Additions to the dataset This month we added two new signals to the data which attempt to show the effect of ad-blockers on the trackers in our database. These signals are: * `has_blocking` - the proportion of pages on which this tracker was affected by some kind of blocking. * `requests_failed` - the average number of failed requests per page load (for comparison with `requests` to get an idea of how aggressive the blocking is). These signals should be able to tell us something about the impact of blocking on different trackers in the ecosystem. For example, we see evidence of blocking 40% of the time for Google Analytics and Facebook, and between 10% and 20% of requests failing. Thus, anyone using these services to measure activity and conversions on their sites must reckon with error rates in these orders. We also can see how new entrants can initially avoid the effects of blocking - for [Tru Optik](../trackers/truoptik.html) and [Digitrust](../trackers/digitrust.html) who we mentioned earlier, we measure only 5 and 1% of pages which may be affected by blocking. These stats are currently only available in the raw data, but we will be looking at incorporating them in the site in due course. ================================================ FILE: blog/update_feb_2018.md ================================================ title: February Update - The Tracking Shell Game subtitle: How mergers and acquisitions are hiding who actually is tracking us. author: privacy team type: article publish: True date: 2018-02-06 tags: blog, update header_img: blog/blog-data-feb18-2.png +++ _This post is one of our regular monthly blogs accompanying an update to the data displayed on WhoTracks.Me. In these posts we introduce what data has been added as well as point out interesting trends and case-studies we found in the last month. Previous month's posts can be found here: [January 2018](./update_jan_2018.html), [December 2017](./update_dec_2017.html)._ We've updated the site today with data collected during January 2018. Due to increased distribution, we have over 115 million page loads this month, an increase of 15% over previous months (see [Where does the data come from?](./where_is_the_data_from.html) for more background on our data collection). The regions from which we are getting data is also diversifying. While 70% of the data still comes from German users, we now have more significant US and international data. We plan to have sufficient data in the coming months in order to provide region-specific tracking breakdowns. ## The tracking shell game Picking out some of the biggest movers in the rankings this month, we first find [Nexage](../trackers/nexage.html) down 262 places this month, and to one tenth of its reach in May last year. This is probably simply a winding down of the operation which was acquired by Millennial Media in 2014, who were acquired by AOL in 2015, who were acquired by Verizon also in 2015. Their landing page now redirects to [One by AOL](https://www.onebyaol.com/). One of the challenges for us on whotracks.me is to make the link between tracker domain names, tracking products, and tracking companies. Nexage is an example of how many trackers lead you down a 'rabbit hole' of mergers and acquisitions until you find the company above it all. If we expand out the web of companies underneath Verizon who are also present on whotracks.me, we find 10 different trackers which can be linked: [Adap.tv](../trackers/adap.tv.html), [ADTECH](../trackers/adtech.html), [Advertising.com](../trackers/advertising.com.html), [alephD](../trackers/alephd.com.html), [Convertro](../trackers/convertro.html), [Nexage](../trackers/nexage.html) and [Vidible](../trackers/vidible.html) under AOL, and [Brightroll](../trackers/brightroll.html) and [Flickr](../trackers/flickr_badge.html) under [Yahoo](../trackers/yahoo.html). Furthermore, Yahoo and AOL both have popular web portals ([yahoo.com](../websites/yahoo.com.html), [aol.com](../websites/aol.com.html) and [aol.de](../websites/aol.de.html)) to drive more traffic which they can track. This leads Verizon to be able to track at least 6% of web traffic, the 11th highest reach of any company in our dataset. You can now check the full list of companies sorted by their trackers' combined reach [here](../companies/reach-chart.html). Verizon's trackers

Verizon's trackers - © WhoTracks.Me 2018

A new entry at 546, [Smarter Travel Media](../trackers/smarter_travel.html) is another example of a smaller company with giants hiding behind it. The tracker is primarly present on tripadvisor and other travel sites, and infact they are a [Tripadvisor](../trackers/tripadvisor.html) company. Tripadvisor in turn is owned by [Expedia](../trackers/expedia.html). Above all of this stands [InterActiveCorp (IAC)](http://iac.com/) who own several other web brands, including [Vimeo](../trackers/vimeo.html) and [Mindspark](../trackers/mindspark.html). Verizon's trackers

IAC - © WhoTracks.Me 2018

The final movers we would like to highlight this month are [davebestdeals.com](../trackers/davebestdeals.com.html) and [eshopcomp.com](../trackers/eshopcomp.com.html), up 201 and 194 places respectively. Unfortunately we cannot yet trace the owners of these trackers---they are both registered with PrivacyGuard in [Panama](https://who.is/whois/eshopcomp.com) and have no visible landing page. In fact they are likely operated by the same entity as their domains point to the same CloudFront endpoints, for example on the `istatic` subdomain for both domains. The reason for the lack of transparency in this case is that they are malware. Looking at their profile pages we can see that they have a small presence across many sites, including sites which we know for certain would not have trackers like this in the page (e.g. Google sites, which will only ever contain Google's own trackers). These are likely browser extensions which include code to inject their tracking code in all of the pages the user visits, and send this information back to their servers. The user browsing history which they collect can then by sold on. The Web Of Trust extension was [caught doing this](https://www.forbes.com/sites/leemathews/2016/11/07/web-of-trust-browser-add-on-blasted-for-breaking-user-trust/#5029a0a53ef5) in 2015, and our data shows that it is still a common practice (look for the 'Extensions' tracker category on this site). This style of user history harvesting also has the advantage that it is not blocked by the majority of Ad-blocking and privacy tools. These domains are not on these blocklists, because the list maintainers will not encounter them - unless they happen to install the malware themselves. Therefore, currently only Cliqz and Ghostery 8's AI anti-tracking are detecting these trackers and preventing them from gathering user sessions - because they are using the same data to find trackers that whotracks.me uses. ## New data points This month we add data about the content-types loaded by trackers. This is based on values reported by the [webRequest 'type' property](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/webRequest/ResourceType). By reporting these values we can further characterise tracker behaviours, and quantify risks, such as which trackers are being permitted to load scripts on certain pages. We add the following new columns for trackers, reported as the proportion of pages where the specific tracker or company loaded particular resource type(s) into the page: * `script`: Javascript code (via a `" ], "text/vnd.plotly.v1+html": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Plotting imports\n", "from plotly.offline import init_notebook_mode, iplot\n", "init_notebook_mode()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Generating Sankey Data\n", "When building the tracker maps that you see on popular site profiles on whotracks.me, sankey diagrams seemed like a good fit to map categories of tracking to companies that own the trackers. Each link would be a tracker, going from a category to a company. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![title](tumblr.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Given we had decided to use plottly.offline to generate the interactive images, I wanted to use the sankey diagram supported in plotly. The fuction itself is pretty straightforward, as you can see in `sankey_diagram()`, but figuring out how the structure of the input data took a bit. Hopefully the following example will make it easier for those reading this post, should they ever decided to try sankey diagrams.\n", "\n", "The goal here is to show some very small dataset, structured in a way that the plotly diagram (and other plotting solutions e.g.: d3.js) understand. We will be mapping cities to the countries they are part of. The value of each link, will be the city population (in millions)." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "city_data = dict(\n", " nodes = dict(\n", " label=[\"Germany\", \"Berlin\", \"Munich\", \"Cologne\", \"France\", \"Paris\", \"Lyon\", \"Bordeaux\"],\n", " color=[\"beige\", \"black\", \"red\", \"yellow\", \"beige\", \"blue\", \"white\", \"red\"]\n", " ),\n", " links = dict(\n", " source=[0, 0, 0, 4, 4, 4],\n", " target=[1, 2, 3, 5, 6, 7],\n", " value= [3.5, 1.5, 1, 2.2, 0.5, 0.2],\n", " label=[\"capital\", \"city\", \"city\", \"capital\", \"city\", \"city\"],\n", " color=[\"black\", \"red\", \"yellow\", \"blue\", \"whitesmoke\", \"red\"]\n", " )\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note how there are two keys in the `dictionary`, `nodes` and `links`, and each has some attributes. Let's go over them. Each node has a label (e.g. `Germany`) and a corresponding color (in this case `beige`). Note than `labels` and `colors` are stored in lists of equal length, and the pairing is done based on the index. \n", "\n", "Links contain information about how to link nodes. Eeach has a `source`, `target`, `value`, `label` and `color`. Source cointains the index in the list of the source node, whereas target the index in the list of the target node. Value determines how thick the link should be (in our case it will be the population of each link, hence each city), Label and color, as the name suggests, specify the label and color of the link. Links too, are paired based on index." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Plotting a sankey diagram\n", "\n", "Now let's write a simple function to plot these data nicely. Most of the work has already been done, given we're feeding the data in a format that's easy to parse." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "def sankey_diagram(sndata, title):\n", " # First part of a plotly plot is the `trace`\n", " data_trace = dict(\n", " type='sankey',\n", " node=dict(\n", " pad=10,\n", " thickness=30,\n", " # label could easily be equal to sndatap['node]['label']. The following is just cosmetics\n", " label=list(map(lambda x: x.replace(\"_\", \" \").capitalize(), sndata['nodes']['label'])),\n", " color=sndata['nodes']['color']\n", " ),\n", " link=sndata[\"links\"],\n", " \n", " # configuration options for the diagram\n", " domain=dict(\n", " x=[0, 1],\n", " y=[0, 1]\n", " ),\n", " hoverinfo=\"none\",\n", " orientation=\"h\"\n", " )\n", " # Second part of a plotly plot is the `layout`\n", " layout = dict(\n", " title=title,\n", " font=dict(\n", " size=12\n", " )\n", " )\n", " fig = dict(data=[data_trace], layout=layout)\n", " return iplot(fig)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Sankey diagram for a few German and French citites\n", "All that is left now, is feeding the city_data to the sankey_diagram function and we're done." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "application/vnd.plotly.v1+json": { "data": [ { "domain": { "x": [ 0, 1 ], "y": [ 0, 1 ] }, "hoverinfo": "none", "link": { "color": [ "black", "red", "yellow", "blue", "whitesmoke", "red" ], "label": [ "capital", "city", "city", "capital", "city", "city" ], "source": [ 0, 0, 0, 4, 4, 4 ], "target": [ 1, 2, 3, 5, 6, 7 ], "value": [ 3.5, 1.5, 1, 2.2, 0.5, 0.2 ] }, "node": { "color": [ "beige", "black", "red", "yellow", "beige", "blue", "white", "red" ], "label": [ "Germany", "Berlin", "Munich", "Cologne", "France", "Paris", "Lyon", "Bordeaux" ], "pad": 10, "thickness": 30 }, "orientation": "h", "type": "sankey" } ], "layout": { "font": { "size": 12 }, "title": "A few European Cities" } }, "text/html": [ "
" ], "text/vnd.plotly.v1+html": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "sankey_diagram(city_data, \"A few European Cities\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# From Cities to Trackers\n", "\n", "Doing Sankey diagrams for cities may have been fun. I am not sure the result of doing the same for trackers on your favorite sites will be equally fun. In fact it may be terrifying. We'll be using public data from whotracks.me to map tracker categories to Companies present on a particular site. Each link will be a tracker the company owns. This gives imediate visual insights on who's watching you an why. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Data Utils from `whotracksme`" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "from whotracksme.data.loader import DataSource\n", "from whotracksme.website.plotting.colors import tracker_category_colors, cliqz_colors\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Loading Data\n", "`DataSource` is a class that provides access to trackers, websites and companies. The functionality of `DataSource` is something we'll be constantly trying to improve and expand. Online tracking is messy enough to analyze, so the tooling should be not." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "DATA = DataSource()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Available entities\n", "\n", "These entities are loaded into DataSource, but an API is provided for some common operations on each of them. For more details, have a look at `whotracksme.data.loader`. As far as we're concerned, we can load them like this:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "trackers = DATA.trackers\n", "sites = DATA.sites\n", "companies = DATA.companies\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Looking at reddit.com\n", "Most people know what reddit is. For you that don't, check it out - there are some great communities there. Now we'll look at the tracking landscape in reddit. To do that, we only need to know the reddit `site_id`, which is `reddit.com`. Each site has a `site_id`, most often its `url`. " ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dict_keys(['apps', 'category', 'history', 'name', 'overview', 'subdomains'])" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "reddit_id = \"reddit.com\"\n", "reddit_data = DATA.sites.get_site(reddit_id)\n", "\n", "# reddit_data is a dictionary. And a site object has the following keys: \n", "reddit_data.keys()\n", "\n", "# apps refers to trackers. Naming is hard, but it'll soon be changed to trackers." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Preparing tracker data for sankey diagram\n", "Here we will be mapping the trackers on reddit to the category they belong to (on the left) and to the companies that own them (on the right). This means each link is a tracker, nodes on the left are categories, and nodes on the right are companies. " ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "def sankey_data(site_id, data=DATA):\n", "\n", " nodes = []\n", " link_source = []\n", " link_target = []\n", " link_value = []\n", " link_label = []\n", "\n", " for (tracker, category, company) in data.sites.trackers_on_site(site_id, data.trackers, data.companies):\n", "\n", " # index of this category in nodes\n", " if category in nodes:\n", " cat_idx = nodes.index(category)\n", " else:\n", " nodes.append(category)\n", " cat_idx = len(nodes) - 1 \n", " \n", " # index of this company in nodes\n", " if company in nodes:\n", " com_idx = nodes.index(company)\n", " else:\n", " nodes.append(company)\n", " com_idx = len(nodes) - 1 \n", " \n", " link_source.append(cat_idx)\n", " link_target.append(com_idx)\n", " link_label.append(tracker[\"name\"])\n", " link_value.append(100.0 * tracker[\"frequency\"])\n", "\n", " label_colors = [tracker_category_colors[l] if l in tracker_category_colors else cliqz_colors[\"purple\"] for l in nodes]\n", "\n", " return dict(\n", " nodes = dict(\n", " label=nodes,\n", " color=label_colors\n", " ),\n", " links = dict(\n", " source=link_source,\n", " target=link_target,\n", " value=link_value,\n", " label=link_label,\n", " color=[\"#dedede\"] * len(link_label)\n", " )\n", " )" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "application/vnd.plotly.v1+json": { "data": [ { "domain": { "x": [ 0, 1 ], "y": [ 0, 1 ] }, "hoverinfo": "none", "link": { "color": [ "#dedede", "#dedede", "#dedede", "#dedede", "#dedede", "#dedede", "#dedede", "#dedede", "#dedede", "#dedede", "#dedede", "#dedede", "#dedede", "#dedede", "#dedede", "#dedede", "#dedede", "#dedede", "#dedede", "#dedede", "#dedede", "#dedede", "#dedede" ], "label": [ "Reddit", "Google Tag Manager", "Google Analytics", "Amazon Associates", "Quantcast", "ScoreCard Research Beacon", "Google", "DoubleClick", "Quantcount", "Google AdServices", "Moat", "Google Syndication", "Google APIs", "OpenX", "Imgur", "Google CDN", "YouTube", "Alexa Metrics", "WikiMedia", "Amazon Web Services", "AppNexus", "InsightExpress", "Advertising.com" ], "source": [ 0, 2, 4, 5, 5, 5, 5, 5, 4, 5, 5, 5, 10, 5, 12, 10, 14, 4, 10, 17, 5, 4, 5 ], "target": [ 1, 3, 3, 6, 7, 8, 3, 3, 7, 3, 9, 3, 3, 11, 13, 3, 3, 15, 16, 6, 18, 19, 20 ], "value": [ 99.58731289613593, 99.0402000255409, 98.2706125110061, 91.97209321082666, 46.30429960814889, 45.09245132106922, 44.410240554909564, 43.818095052459654, 37.652657261343855, 32.872476996390674, 20.00053770306692, 16.99612181662981, 16.054469320679388, 8.754478058354225, 5.226473810499996, 4.598705479866381, 2.9412357760735577, 2.19920554371862, 1.9437965869297826, 1.2777169127778412, 1.2004220969075352, 1.1789139742305805, 1.1110289620314422 ] }, "node": { "color": [ "#87BCEF", "#A069AB", "#FC9834", "#A069AB", "#84D7F0", "#BF90D2", "#A069AB", "#A069AB", "#A069AB", "#A069AB", "#C0BB61", "#A069AB", "#80C87D", "#A069AB", "#F86D4F", "#A069AB", "#A069AB", "#444", "#A069AB", "#A069AB", "#A069AB" ], "label": [ "Social media", "Reddit", "Essential", "Google", "Site analytics", "Advertising", "Amazon", "Quantcast", "Comscore", "Oracle", "Cdn", "Openx", "Misc", "Imgur", "Audio video player", "Alexa", "Wikimedia", "Hosting", "Appnexus", "Millward brown", "Aol" ], "pad": 10, "thickness": 30 }, "orientation": "h", "type": "sankey" } ], "layout": { "font": { "size": 12 }, "title": "reddit.com" } }, "text/html": [ "
" ], "text/vnd.plotly.v1+html": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "input_data = sankey_data(reddit_id, data=DATA)\n", "sankey_diagram(input_data, reddit_id)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't forget to check out the article on https://whotracks.me/blog/trackers_in_your_favorite_site.html" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.2" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: contrib/wtm_april_update.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Whotracks.me April Update\n", "\n", "This month we have a big update to the site. We have restructured the data we publish to make it easier to use, increased the number of entries we publish, and we have laid the groundwork for internationalised versions of WhoTracks.Me - that means you can see how tracking differs between different countries.\n", "\n", "Thanks to integration with Ghostery 8 we collected significantly more tracker data this month, covering 360 million page loads. This is spread over countries across the world, with Germany and the USA the most represented." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/vnd.plotly.v1+html": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from plotly.offline import init_notebook_mode, iplot, offline\n", "import plotly.graph_objs as go\n", "from whotracksme.website.plotting.colors import cliqz_colors, palette\n", "from whotracksme.website.plotting.utils import (\n", " WTMFonts,\n", " div_output,\n", " set_margins,\n", " annotation,\n", " set_line_style,\n", " set_category_colors\n", ")\n", "\n", "import pandas as pd\n", "init_notebook_mode()" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "application/vnd.plotly.v1+json": { "data": [ { "hole": 0.45, "hoverinfo": "label+percent", "labels": [ "Germany", "USA", "France", "Other", "Russia", "UK", "Poland", "Netherlands", "Canada", "Ukraine", "Austria", "Italy", "Spain", "Switzerland", "Belgium" ], "name": "Data origin", "pull": 0.07, "textfont": { "color": "#1A1A25", "family": "sans-serif", "size": 15 }, "textinfo": "label", "textposition": "outside", "type": "pie", "values": [ 87124064, 78216572, 40282874, 32326828, 24384449, 16317893, 10554555, 10291928, 10054367, 6268086, 6261035, 6094486, 5753209, 4732324, 4048089 ] } ], "layout": { "annotations": [ { "align": "center", "ax": 0, "ay": 0, "bgcolor": "#1A1A25", "bordercolor": "#1A1A25", "borderpad": 5, "borderwidth": 1, "font": { "color": "white", "family": "sans-serif", "size": 15 }, "showarrow": true, "text": "DATA ORIGIN", "width": 100, "x": 0.5, "xref": "x", "y": 0.5, "yref": "y" } ], "margin": { "b": 30, "l": 60, "pad": 5, "r": 60, "t": 30 }, "paper_bgcolor": "#00000000", "plot_bgcolor": "#FFFFFF", "showlegend": false, "xaxis": { "showgrid": false, "showline": false, "showticklabels": false, "zeroline": false }, "yaxis": { "showgrid": false, "showline": false, "showticklabels": false, "zeroline": false } } }, "text/html": [ "
" ], "text/vnd.plotly.v1+html": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "def doughnut_chart(values, labels, name):\n", " trace = go.Pie(\n", " values=values,\n", " labels=labels,\n", " name=str(name),\n", " hoverinfo=\"label+percent\",\n", " textposition=\"outside\",\n", " hole=0.45,\n", " pull=0.07,\n", " textinfo=\"label\",\n", " textfont=dict(\n", " family=WTMFonts.regular,\n", " color=cliqz_colors[\"black\"],\n", " size=15\n", " ) \n", " )\n", " data = [trace]\n", " layout = dict(\n", " showlegend=False,\n", " paper_bgcolor=cliqz_colors[\"transparent\"],\n", " plot_bgcolor=cliqz_colors[\"white\"],\n", " xaxis=dict(showgrid=False, showline=False, showticklabels=False, zeroline=False),\n", " yaxis=dict(showgrid=False, showline=False, showticklabels=False, zeroline=False),\n", " # autosize=True,\n", " margin=set_margins(t=30, b=30),\n", " annotations=[\n", " annotation(\n", " text=str(name).upper(),\n", " x=0.5,\n", " y=0.5,\n", " background_color=cliqz_colors[\"black\"],\n", " shift_x=0,\n", " text_size=15\n", " )\n", " ]\n", " )\n", " fig = dict(data=data, layout=layout)\n", " # NB: saving plot requires a manual step, plotly is does not support it yet\n", " # source: https://github.com/plotly/plotly.py/issues/880\n", " offline.plot(fig, image='svg')\n", "\n", " return iplot(fig)\n", "\n", "countries = ['Germany', 'USA', 'France', 'Other', 'Russia', 'UK', 'Poland', 'Netherlands', 'Canada', 'Ukraine', 'Austria', 'Italy', 'Spain', 'Switzerland', 'Belgium']\n", "page_loads = [87124064, 78216572, 40282874, 32326828, 24384449, 16317893, 10554555, 10291928, 10054367, 6268086, 6261035, 6094486, 5753209, 4732324, 4048089]\n", "\n", "doughnut_chart(values=page_loads, labels=countries, name='Data origin')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This volume of data will also enable us to publish separate rankings for individual countries, something we plan to add later this month." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Data restructure\n", "\n", "We have updated the struture of data which we publish in our [respository](https://github.com/ghostery/whotracks.me/) to make it both easier to use and more scalable as we add more data. We now publish CSV files each month for each of the following:\n", "\n", " * `domains.csv`: Top third-party domains seen tracking.\n", " * `trackers.csv`: Top trackers - this combines domains known be operated by the same tracker.\n", " * `companies.csv`: Top companies - aggregates the stats for trackers owned by the same company.\n", " * `sites.csv`: Stats for number of trackers seen on popular websites.\n", " * `site_trackers.csv`: Stats for each tracker on each site.\n", "\n", "These files can then be loaded with popular data-analysis tools such as [Pandas](https://pandas.pydata.org/). We have also rewritten the code to render the site to take advantage of Pandas. We expose the dataframes via the `DataSource` class which loads data from all CSV files:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from whotracksme.data.loader import DataSource\n", "data = DataSource()\n", "len(data.trackers.df)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We have also updated the criteria by which we include trackers and sites on the main site. We now 'rollover' entries, so once they have been included once, we will keep publishing data (until they completely dissappear from the data). This has the effect of naturally growing the number of trackers and sites we publish. We currently have data on 868 trackers and 748 websites published:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def plot_ts():\n", " df = pd.DataFrame({\n", " 'trackers': data.trackers.df.groupby('month').count()['tracker'], \n", " 'sites': data.sites.df.groupby('month').count()['site']\n", " })\n", " sites_trace = go.Scatter(\n", " x=df.index, \n", " y=df.sites, \n", " name='Sites',\n", " line=dict(width=4, color='#9ebcda'),\n", " )\n", " trackers_trace = go.Scatter(\n", " x=df.index, \n", " y=df.trackers, \n", " name='Trackers',\n", " line=dict(width=4, color='#A069AB'),\n", " )\n", " \n", " layout=dict(\n", " margin=set_margins(t=0,b=30),\n", " legend=dict(\n", " x=0.05, y=1,\n", " bgcolor='#E2E2E2',\n", " orientation='h'\n", " )\n", " )\n", " fig = dict(data=[sites_trace, trackers_trace], layout=layout)\n", " offline.plot(fig, image='svg')\n", "\n", " iplot(fig)\n", "\n", "plot_ts()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The per site trend for average number of trackers continues a slightly downward trend, but the average is still above 9. There are several possible reasons for this, it is not necessarily that sites are using fewer trackers. The proportion of data from Ghostery users continues to increase, and these users will disproportionately block many trackers. This has an effect on the average number of trackers, because it prevents the blocked trackers from loading others. The data shows also that the average indcidence of blocking for trackers increased to 25% in March, up from 20% in February. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "traces = [\n", " go.Box(\n", " y=data.sites.df[data.sites.df.month == '2018-01'].trackers, \n", " name='Jan 2018',\n", " marker=dict(\n", " color='#c44e52',\n", " line=dict(\n", " color='#c44e52',\n", " width=3\n", " ),\n", " )\n", " ),\n", " go.Box(\n", " y=data.sites.df[data.sites.df.month == '2018-02'].trackers, \n", " name='Feb 2018',\n", " marker=dict(\n", " color='#55a868',\n", " line=dict(\n", " color='#55a868',\n", " width=3\n", " ),\n", " )\n", " ),\n", " go.Box(\n", " y=data.sites.df[data.sites.df.month == '2018-03'].trackers, \n", " name='Mar 2018',\n", " marker=dict(\n", " color='#4c72b0',\n", " line=dict(\n", " color='#4c72b0',\n", " width=3\n", " ),\n", " )\n", " )\n", "]\n", "fig = dict(data=traces, layout=dict(showlegend=False, margin=set_margins(t=0, b=30)))\n", "offline.plot(fig, image='svg')\n", "iplot(fig)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Mean occurrence of Blocking per page\n", "traces = [\n", " go.Bar(\n", " x=['Jan 2018', 'Feb 2018', 'Mar 2018'],\n", " y=[\n", " data.trackers.df[data.trackers.df.month == '2018-01'].has_blocking.mean()*100,\n", " data.trackers.df[data.trackers.df.month == '2018-02'].has_blocking.mean()*100,\n", " data.trackers.df[data.trackers.df.month == '2018-03'].has_blocking.mean()*100\n", " ],\n", " marker=dict(\n", " color=['#A069AB', '#9564c4', '#6564c4'],\n", " line=dict(\n", " color='#222',\n", " width=2\n", " ),\n", " )\n", " )\n", "]\n", "fig = dict(data=traces, layout=dict(margin=set_margins(t=0, b=30)))\n", "offline.plot(fig, image_height=200, image_width=800, image='svg', output_type='file')\n", "iplot(fig)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As in previous months, we look at sites' changing the trackers. [fewo-direct.de](../websites/fewo-direkt.de.html), [brigitte.de](../websites/brigitte.de.html) and [gutefrage.net](../websites/gutefrage.net.html) all had 5 fewer trackers on average per page this month. However, each of these still has over 50 trackers with some kind of presence, showing that this is more likely a side-effect of increased blocking than an active effort to reduce tracking on their sites. [klingel.de](../websites/klingel.de.html) and [informationvine.com](../websites/informationvine.com.html) see the largest increase in tracking of the sites we currently monitor." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mar_trackers = data.sites.get_snapshot('2018-03').set_index('site')['trackers']\n", "feb_trackers = data.sites.get_snapshot('2018-02').set_index('site')['trackers']\n", "site_diffs = pd.DataFrame({\n", " 'trackers': mar_trackers,\n", " 'change': (mar_trackers - feb_trackers)\n", "})\n", "site_diffs[(site_diffs.change > 5) | (site_diffs.change < -5.5)].sort_values('change')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A side-effect of the filtering we added in this new data pipeline is that the site reach for top trackers has increased. In the previous analysis a long-tail of very rarely visited sites reduced effective site reach. With this factor reduced, we get a real sense of the coverage of the largest trackers, with Google Analytics reaching 85% of popular sites, and Facebook almost 60%." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df = data.trackers.get_snapshot().sort_values(by='site_reach', ascending=False).head(10)\n", "df['name'] = df.id.apply(func=lambda x: data.app_info[x]['name'])\n", "\n", "traces = [\n", " go.Bar(\n", " x=df.site_reach[::-1]*100,\n", " y=df.name[::-1],\n", " orientation='h',\n", " marker=dict(\n", " color=palette('#9ebcda', '#A069AB', 10),\n", " line=dict(\n", " color='#333',\n", " width=2\n", " ),\n", " )\n", " )\n", "]\n", "layout=dict(margin=set_margins(l=200))\n", "fig = dict(data=traces, layout=layout)\n", "offline.plot(fig, image='svg')\n", "iplot(fig)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you want to delve deeper into our data, it is available on the [Whotracks.me Github Repository](https://github.com/ghostery/whotracks.me/tree/master/whotracksme/data), and as a [pip package](https://pypi.python.org/pypi/whotracksme/)." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.0" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: contrib/wtm_may_update.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Whotracks.me May Update\n", "\n", "*This post is one of our regular monthly blogs accompanying an update to the data\n", "displayed on WhoTracks.Me. In these posts we introduce what data has been added as well\n", "as point out interesting trends and case-studies we found in the last month. Previous\n", "month's posts can be found here: [April 2018](./update_apr_2018.html),\n", "[February 2018](./update_feb_2018.html), [January 2018](./update_jan_2018.html),\n", "[December 2017](./update_dec_2017.html).*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "This month we update the site with data from 340 million page loads during April 2018. We expand\n", "the number of trackers shown to 951, and the number of websites to 1330. As this will be the last\n", "full month before the [GDPR](https://en.wikipedia.org/wiki/General_Data_Protection_Regulation)\n", "comes into force for European users, this will provide a benchmark to assess whether there is an\n", "observable difference on the tracking ecosystem.\n", "\n", "This month also saw our new paper **\"WhoTracks.Me: Monitoring the online tracking landscape at scale\"**\n", "published on [Arxiv](https://arxiv.org/abs/1804.08959). This paper covers the methodology behind\n", "the data we collect here, and how we ensure no private information can be leaked during this\n", "process.\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/vnd.plotly.v1+html": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from plotly.offline import init_notebook_mode, iplot, offline\n", "\n", "import pandas as pd\n", "import cufflinks as cf\n", "\n", "init_notebook_mode()\n", "cf.set_config_file(offline=False, world_readable=True, theme='pearl')" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "data available for months: ['2017-05', '2017-06', '2017-07', '2017-08', '2017-09', '2017-10', '2017-11', '2017-12', '2018-01', '2018-02', '2018-03', '2018-04']\n" ] } ], "source": [ "from whotracksme.data.loader import DataSource\n", "data = DataSource()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Notable Changes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As customary, here below are the sites with the most notable changes this month. The\n", "largest increase in the average number of trackers per page load was measured in\n", "[markt.de](https://whotracks.me/websites/markt.de.html), and the largest decrease in\n", "[babbel.com](https://whotracks.me/websites/babbel.com.html)." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
changetrackers
babbel.com-8.11145412.722951
bento.de-3.61172319.215815
klingel.de-3.49289326.706119
tvnow.de-3.15107325.500678
sheego.de4.63352611.616530
markt.de10.79591117.783326
\n", "
" ], "text/plain": [ " change trackers\n", "babbel.com -8.111454 12.722951\n", "bento.de -3.611723 19.215815\n", "klingel.de -3.492893 26.706119\n", "tvnow.de -3.151073 25.500678\n", "sheego.de 4.633526 11.616530\n", "markt.de 10.795911 17.783326" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "apr_trackers = data.sites.get_snapshot('2018-04').set_index('site')['trackers']\n", "mar_trackers = data.sites.get_snapshot('2018-03').set_index('site')['trackers']\n", "site_diffs = pd.DataFrame({\n", " 'trackers': mar_trackers,\n", " 'change': (apr_trackers - mar_trackers)\n", "})\n", "site_diffs[(site_diffs.change > 3) | (site_diffs.change < -3)].sort_values('change')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Facebook's Tough Month\n", "\n", "[Facebook](../trackers/facebook.html) have been in the news a lot in the last month, and with\n", "the `#deletefacebook` trending, will there have been an effect on their operations and bottom\n", "line? We [already reported](https://www.ghostery.com/blog/ghostery-news/report-have-publishers-banned-facebook-trackers-from-their-pages-after-the-cambridge-analytica-scandal/)\n", "that despite strong criticism in the press, the same news sites did not stop using Facebook's\n", "tracking tools. The data we release this month shows that this continues to be the case, with no\n", "drop in tracking reach for the [Facebook tracker](../trackers/facebook.html).\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "facebookDf = data.trackers.df[\n", " (data.trackers.df.tracker == \"facebook\") \n", "# & (data.trackers.df.month >= \"2018-01\")\n", "]\n", "facebookDf = facebookDf[['month','reach', 'site_reach']]\n" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "application/vnd.plotly.v1+json": { "data": [ { "fill": "tozeroy", "fillcolor": "rgba(255, 153, 51, 0.3)", "line": { "color": "rgba(255, 153, 51, 1.0)", "dash": "solid", "width": 1.3 }, "mode": "lines", "name": "reach", "text": "", "type": "scatter", "x": [ "2017-05-01", "2017-06-01", "2017-07-01", "2017-08-01", "2017-09-01", "2017-10-01", "2017-11-01", "2017-12-01", "2018-01-01", "2018-02-01", "2018-03-01", "2018-04-01" ], "xaxis": "x1", "y": [ 0.31346037651773023, 0.3153770862904671, 0.30586649894717577, 0.30095937714542365, 0.2980006216898452, 0.2862098839766894, 0.28746809741487106, 0.2823794258106336, 0.2841597964515066, 0.2879803049264181, 0.2856411110740228, 0.28704290575311786 ], "yaxis": "y1" }, { "fill": "tozeroy", "fillcolor": "rgba(55, 128, 191, 0.3)", "line": { "color": "rgba(55, 128, 191, 1.0)", "dash": "solid", "width": 1.3 }, "mode": "lines", "name": "site_reach", "text": "", "type": "scatter", "x": [ "2017-05-01", "2017-06-01", "2017-07-01", "2017-08-01", "2017-09-01", "2017-10-01", "2017-11-01", "2017-12-01", "2018-01-01", "2018-02-01", "2018-03-01", "2018-04-01" ], "xaxis": "x1", "y": [ 0.37418853060533264, 0.378439338380774, 0.3779495953148648, 0.3755754129434065, 0.3738063981206384, 0.3719620646574803, 0.3763100284130607, 0.3848586709051076, 0.3896124423089081, 0.40593970425354137, 0.4148203788481679, 0.4060915662353362 ], "yaxis": "y2" } ], "layout": { "legend": { "bgcolor": "#F5F6F9", "font": { "color": "#4D5663" } }, "paper_bgcolor": "#F5F6F9", "plot_bgcolor": "#F5F6F9", "shapes": [ { "line": { "color": "#db4052", "dash": "solid", "width": 1 }, "type": "line", "x0": "2018-03", "x1": "2018-03", "xref": "x", "y0": 0, "y1": 1, "yref": "paper" } ], "title": "Reach and Site Reach", "titlefont": { "color": "#4D5663" }, "xaxis1": { "anchor": "y2", "domain": [ 0, 1 ], "gridcolor": "#E1E5ED", "showgrid": true, "tickfont": { "color": "#4D5663" }, "title": "", "titlefont": { "color": "#4D5663" }, "zerolinecolor": "#E1E5ED" }, "yaxis1": { "anchor": "free", "domain": [ 0.575, 1 ], "gridcolor": "#E1E5ED", "position": 0, "showgrid": true, "tickfont": { "color": "#4D5663" }, "title": "", "titlefont": { "color": "#4D5663" }, "zerolinecolor": "#E1E5ED" }, "yaxis2": { "anchor": "x1", "domain": [ 0, 0.425 ] } } }, "text/html": [ "
" ], "text/vnd.plotly.v1+html": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig = facebookDf.iplot(\n", " subplots=True,\n", " shape=(2, 1),\n", " x='month',\n", " shared_xaxes=True, \n", " fill=True,\n", " title=\"Reach and Site Reach\",\n", " vline=[\"2018-03\"],\n", " asFigure=True\n", ")\n", "\n", "# fig.iplot()\n", "\n", "# To save the image as svg\n", "offline.iplot(fig, image='svg')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that `reach` refers to the percentage of total page loads where the Facebook\n", "tracker was seen to be present, whereas `site reach` refers to the percentage of\n", "domains.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Google and the Countdown to GDPR" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With GDPR coming into effect on 25th May, we will soon see if it has an impact on the number of\n", "third-party trackers loaded on web pages. [Recent reports indicate](https://adexchanger.com/online-advertising/googles-gdpr-consent-tool-will-limit-publishers-to-12-ad-tech-vendors/)\n", "that Google will encourage publishers to reduce the number of AdTech vendors they use, in order to\n", "increase the chance of getting consent for tracking from users. If this is the case, we should\n", "expect this change to be visible in the WhoTracks.Me data." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "dc_sites = data.sites_trackers.df[\n", " (data.sites_trackers.df.tracker == \"doubleclick\")\n", " & (data.sites_trackers.df.month == \"2018-04\")\n", " & (data.sites_trackers.df.country == \"global\")\n", " & (data.sites_trackers.df.site_proportion > 0.5)\n", "].site\n", "\n", "\n", "dc_sites_df = data.sites.df[\n", " (data.sites.df.site.isin(dc_sites))\n", " & (data.sites.df.month >= \"2018-02\")\n", "]\n", "\n", "\n", "dcsitesDf = pd.DataFrame({\n", " \"apr_trackers\": dc_sites_df[dc_sites_df.month == '2018-04'].trackers,\n", " \"mar_trackers\": dc_sites_df[dc_sites_df.month == '2018-03'].trackers,\n", "})" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig = dcsitesDf.iplot(\n", " kind=\"histogram\",\n", " histnorm='percent',\n", " title=\"Distribution of the average number of trackers per site\",\n", " opacity=.6,\n", " bins=20,\n", " yTitle=\"Percentage of Sites\",\n", " vline={\n", " \"kind\": \"rect\",\n", " \"x0\": 12,\n", " \"x1\": 38,\n", " \"width\": 2,\n", " \"fillcolor\": \"red\",\n", " \"opacity\": 0.1\n", " },\n", " barmode=\"overlay\",\n", " bargap=0.2,\n", " line_color=\"#00000000\",\n", " width=0,\n", " asFigure=True,\n", ")\n", "\n", "fig.iplot()\n", "\n", "# To save the image as svg\n", "offline.plot(fig, image='svg')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As we reported [last month](./update_apr_2018.html), we observe a gradual decline in the average\n", "number of trackers seen on websites. However, looking at sites which use Google's [Doubleclick](../trackers/doubleclick.html)\n", "Ad Network, a large proportion are still well-above this proposed 12 tracker limit. With only a few\n", "weeks to go, there will still be be a significant number of sites over the limit." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we were to consider the most extreme scenario, where Google compels all customers use their GDPR\n", "consent system for European users, and enforces a 12 vendor limit in the process, this could\n", "have a significant impact on the ecosystem. If we extrapolate from WhoTracks.Me data, capping all\n", "these sites to 12 trackers means that over **1,300 trackers** would disappear from sites. AdTech\n", "companies deeper in the supply chain may be completely cut out unless they have direct publisher\n", "relationships which enable them to make the vendor shortlist.\n", "\n", "Such a sharp change in the ecosystem is unlikely, but it demonstrates the power of Google's market\n", "dominance, that they would be able to unilaterally pull the plug on a lot of their competition. We\n", "will continue to monitor the ecosystem to quantify any changes to tracking, and look forward to\n", "reporting the changes, if any, caused by the new regulation." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you want to delve deeper, the data is open and available on the [Whotracks.me Github Repository](https://github.com/ghostery/whotracks.me/tree/master/whotracksme/data), and as a [pip package](https://pypi.python.org/pypi/whotracksme/)." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.5" } }, "nbformat": 4, "nbformat_minor": 2 } ================================================ FILE: deploy_to_s3.py ================================================ """ Module to deploy WhoTracksMe site to an s3 bucket. Usage: deploy_to_s3 [] [--production] [--no-overrides] [--skip-old-years] [--fast-website-update] [--update-api-only] [--list-outdated] [--verbose] [--dry-run] [--debug] Options: -h, --help Show help message. --production Production deployment (set cache-control metadata) [Default: true] --no-overrides Skip files that already exist on S3 [Default: false] --skip-old-years Do not upload data files of the previous years [Default: false] --fast-website-update Fast mode if you want to update static resources (e.g. blog post, updating privacy policy) without uploading all data files [Default: false] --update-api-only Skip all files except for the API files [Default: false] --list-outdated List all files which are present on S3, yet came from an older release [Default: false] --verbose Enable debug logs [Default: false] --dry-run Do not perform modifications [Default: false] --debug Disable parallel workers to use a debugger [Default: false] """ import os import boto3 import re import datetime from docopt import docopt from mimetypes import MimeTypes from concurrent.futures import ProcessPoolExecutor def iterate_bucket(s3_client, bucket_name, bucket_prefix): pageinator = s3_client.get_paginator('list_objects_v2') for page in pageinator.paginate(Bucket=bucket_name, Prefix=bucket_prefix): if page['KeyCount'] == 0: continue for item in page['Contents']: yield item['Key'] def get_max_age(path, filename): if path.startswith('static/'): # font resources: one month cache if 'font-awesome-4.7.0' in path or 'fonts' in path: return 2592000 # minified JS: one week cache if '.min.js' in filename or '.min.css' in filename: return 604800 # one week cache return 604800 elif path.startswith('data/'): # trackerdb: 1day cache if 'trackerdb.' in path: return 86400 # rest of data directory: 1 week cache return 604800 # default 1day cache for html pages return 86400 def get_cache_control(path, filename, production=False): if not production: # one minute cache return 'max-age=60' return f'max-age={get_max_age(path, filename)}' def get_content_type(local_path): mime = MimeTypes() guessed_type = mime.guess_type(local_path)[0] if guessed_type is None: if local_path.endswith('.css.map') or local_path.endswith('.js.map'): return 'application/json' elif local_path.endswith('.otf'): return 'font/opentype' elif local_path.endswith('.eot'): return 'application/vnd.ms-fontobject' elif local_path.endswith('.ttf'): return 'application/font-sfnt' elif local_path.endswith('.woff'): return 'application/font-woff' elif local_path.endswith('.woff2'): return 'application/font-woff2' elif local_path.endswith('.zip'): return 'application/zip' elif local_path.endswith('.pack'): return 'application/octet-stream' return 'text/html' return guessed_type if __name__ == '__main__': args = docopt(__doc__) bucket_name = args[''] bucket_prefix = args[''] or '/' production = args['--production'] no_overrides = args['--no-overrides'] or False skip_old_years = args['--skip-old-years'] or False fast_website_update = args['--fast-website-update'] or False update_api_only = args['--update-api-only'] or False list_outdated = args['--list-outdated'] or False verbose = args['--verbose'] or False dry_run = args['--dry-run'] or False debug_mode = args['--debug'] or False site_dir = './_site' if fast_website_update and update_api_only: print('--fast-website-update and --update-api-only cannot be used together') exit(1) if bucket_prefix[0] != '/': bucket_prefix = '/' + bucket_prefix print('Deploying site to ', f's3://{bucket_name}{bucket_prefix}') # check site build exists if not os.path.isdir(site_dir): print('_site directory does not exist. You need to build the site before uploading') exit(1) # connect s3 s3_client = boto3.client('s3') # list existing bucket contents existing_keys = set(iterate_bucket(s3_client, bucket_name, bucket_prefix[1:])) obsolete_files = set(existing_keys) print('Bucket contains', len(existing_keys), 'pages') uploaded = 0 redirected = 0 def upload_file_to_s3(path, filename): # path to local file local_path = os.path.join(path, filename) # relative folder of file s3_suffix = path[len(site_dir) + 1:] # full path from root of bucket s3_path = os.path.join(bucket_prefix, s3_suffix, filename)[1:] # metadata to set on file cache_control = get_cache_control(s3_suffix, filename, production=production) content_type = get_content_type(local_path) if s3_path in existing_keys and no_overrides: if verbose: print(f'Skipping file {s3_path} (already present)...') else: print('put', local_path, s3_path) with open(local_path, 'rb') as fp: should_update = True if update_api_only and not local_path.startswith(f'{site_dir}/api/'): print(f'Skipping non api file {s3_path} (--update-api-only)') should_update = False if fast_website_update: directories_to_skip = [ f'{site_dir}/trackers/', f'{site_dir}/websites/', f'{site_dir}/companies/', f'{site_dir}/data/', f'{site_dir}/api/', ] for directory in directories_to_skip: if local_path.startswith(directory): print(f'Skipping data file {s3_path} (--fast-website-update)') should_update = False break if skip_old_years: # Note: include last December, so the option doesn't break a December release (which happens on January) current_year = datetime.datetime.now().year if (re.match(fr"^{site_dir}/data/\d{{4}}-\d{{2}}/", local_path) and not local_path.startswith(f'{site_dir}/data/{current_year}') and not local_path.startswith(f'{site_dir}/data/{current_year - 1}-12')): print(f'Skipping data file {s3_path} (--skip_old_years)') should_update = False if should_update and not dry_run: s3_client.put_object(Bucket=bucket_name, Key=s3_path, Body=fp, CacheControl=cache_control, ContentType=content_type, ACL='public-read') obsolete_files.discard(s3_path) # setup redirects html_path = f'{s3_path}.html' if html_path in existing_keys: print(f'redirect {html_path} to /{s3_path}') if not dry_run: s3_client.put_object(Bucket=bucket_name, Key=html_path, WebsiteRedirectLocation=f'/{s3_path}', ACL='public-read') # upload + redirect return True, True else: # upload, no redirect return True, False if debug_mode: for (dirpath, dirnames, filenames) in os.walk(site_dir): if verbose: print('Enter', dirpath) files_to_upload = [f for f in filenames if not f[0] == '.'] for filename in files_to_upload: upload_file_to_s3(dirpath, filename) else: with ProcessPoolExecutor(max_workers=8) as executor: uploads = [] for (dirpath, dirnames, filenames) in os.walk(site_dir): if verbose: print('Enter', dirpath) files_to_upload = [f for f in filenames if not f[0] == '.'] for filename in files_to_upload: uploads.append(executor.submit(upload_file_to_s3, dirpath, filename)) for future in uploads: did_upload, did_redirect = future.result() if did_upload: uploaded += 1 if did_redirect: redirected += 1 print(f'Complete: uploaded {uploaded}, redirected {redirected}') if list_outdated: if len(obsolete_files) == 0: print('No obsolete files found.') else: print(f'{len(obsolete_files)} obsolete files found.') if verbose: for f in sorted(obsolete_files): print(f'- {f}') if dry_run: print('[dry-run]: no changes have been made.') ================================================ FILE: docs/local-build.md ================================================ # Local development ## Building the website (the static HTML based version) and the internal API The code to build the website on https://www.ghostery.com/whotracksme is not public; but for local testing, you can still use the code for the [previous version](https://web.archive.org/web/20240501140903/https://whotracks.me/). If you have not done so, make sure that you have downloaded data (see [Data Readme](../whotracksme/data/Readme.md)). To build the website: ``` uv sync --frozen . .venv/bin/activate whotracksme website ``` or ``` uv run --frozen whotracksme ``` It will generate static HTML files in the `_site` directory. Plus, it will also create a JSON files in the `_site/api/` directory. Use them at your own risk, since the format is expected to change over time. If there is interest to stabilize the API files, let us know. Currently, it is only used internally within Ghostery to power the new website. > Hint: if you debug the website generator, the parallel execution can be > disabled by setting the environment variable DEBUG=1. ## Tests To run the unit tests: ```sh uv run --frozen pytest ``` ================================================ FILE: pyproject.toml ================================================ [project] name = "whotracks" version = "2026.4.1" description = "Learn about tracking technologies, market structure and data-sharing on the web" readme = "README.md" license = "MIT" license-files = ["LICENSE.md"] requires-python = ">=3.13" dependencies = [ "aiohttp==3.13.5", "certifi==2025.11.12", "chardet==3.0.4", "colour==0.1.5", "docopt-ng==0.9.0", "feedgen==1.0.0", "idna==2.10", "jinja2==3.1.6", "markdown==3.10.2", "markupsafe==3.0.3", "numpy==2.3.5", "pandas==2.3.3", "python-dateutil==2.9.0", "pytz==2020.1", "requests==2.31.0", "sanic==25.12.0", "six==1.17.0", "squarify==0.4.4", "urllib3==2.5.0", "wheel==0.45.1", "simplejson==3.19.2", "plotly==6.5.0", "async-timeout==5.0.1", "boto3==1.42.0", "watchdog==6.0.0", "libsass==0.23.0", ] [project.scripts] whotracksme = "whotracksme.main:main" [dependency-groups] dev = ["pytest"] [tool.uv] package = true [tool.setuptools.packages.find] where = ["."] include = ["whotracksme*"] exclude = ["_site", "blog", "contrib", "data", "static", "templates", "tests"] ================================================ FILE: static/font-awesome-4.7.0/HELP-US-OUT.txt ================================================ I hope you love Font Awesome. If you've found it useful, please do me a favor and check out my latest project, Fort Awesome (https://fortawesome.com). It makes it easy to put the perfect icons on your website. Choose from our awesome, comprehensive icon sets or copy and paste your own. Please. Check it out. -Dave Gandy ================================================ FILE: static/font-awesome-4.7.0/css/font-awesome.css ================================================ /*! * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) */ /* FONT PATH * -------------------------- */ @font-face { font-family: 'FontAwesome'; src: url('../fonts/fontawesome-webfont.eot?v=4.7.0'); src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg'); font-weight: normal; font-style: normal; } .fa { display: inline-block; font: normal normal normal 14px/1 FontAwesome; font-size: inherit; text-rendering: auto; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } /* makes the font 33% larger relative to the icon container */ .fa-lg { font-size: 1.33333333em; line-height: 0.75em; vertical-align: -15%; } .fa-2x { font-size: 2em; } .fa-3x { font-size: 3em; } .fa-4x { font-size: 4em; } .fa-5x { font-size: 5em; } .fa-fw { width: 1.28571429em; text-align: center; } .fa-ul { padding-left: 0; margin-left: 2.14285714em; list-style-type: none; } .fa-ul > li { position: relative; } .fa-li { position: absolute; left: -2.14285714em; width: 2.14285714em; top: 0.14285714em; text-align: center; } .fa-li.fa-lg { left: -1.85714286em; } .fa-border { padding: .2em .25em .15em; border: solid 0.08em #eeeeee; border-radius: .1em; } .fa-pull-left { float: left; } .fa-pull-right { float: right; } .fa.fa-pull-left { margin-right: .3em; } .fa.fa-pull-right { margin-left: .3em; } /* Deprecated as of 4.4.0 */ .pull-right { float: right; } .pull-left { float: left; } .fa.pull-left { margin-right: .3em; } .fa.pull-right { margin-left: .3em; } .fa-spin { -webkit-animation: fa-spin 2s infinite linear; animation: fa-spin 2s infinite linear; } .fa-pulse { -webkit-animation: fa-spin 1s infinite steps(8); animation: fa-spin 1s infinite steps(8); } @-webkit-keyframes fa-spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg); } } @keyframes fa-spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg); } } .fa-rotate-90 { -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)"; -webkit-transform: rotate(90deg); -ms-transform: rotate(90deg); transform: rotate(90deg); } .fa-rotate-180 { -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)"; -webkit-transform: rotate(180deg); -ms-transform: rotate(180deg); transform: rotate(180deg); } .fa-rotate-270 { -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)"; -webkit-transform: rotate(270deg); -ms-transform: rotate(270deg); transform: rotate(270deg); } .fa-flip-horizontal { -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)"; -webkit-transform: scale(-1, 1); -ms-transform: scale(-1, 1); transform: scale(-1, 1); } .fa-flip-vertical { -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"; -webkit-transform: scale(1, -1); -ms-transform: scale(1, -1); transform: scale(1, -1); } :root .fa-rotate-90, :root .fa-rotate-180, :root .fa-rotate-270, :root .fa-flip-horizontal, :root .fa-flip-vertical { filter: none; } .fa-stack { position: relative; display: inline-block; width: 2em; height: 2em; line-height: 2em; vertical-align: middle; } .fa-stack-1x, .fa-stack-2x { position: absolute; left: 0; width: 100%; text-align: center; } .fa-stack-1x { line-height: inherit; } .fa-stack-2x { font-size: 2em; } .fa-inverse { color: #ffffff; } /* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen readers do not read off random characters that represent icons */ .fa-glass:before { content: "\f000"; } .fa-music:before { content: "\f001"; } .fa-search:before { content: "\f002"; } .fa-envelope-o:before { content: "\f003"; } .fa-heart:before { content: "\f004"; } .fa-star:before { content: "\f005"; } .fa-star-o:before { content: "\f006"; } .fa-user:before { content: "\f007"; } .fa-film:before { content: "\f008"; } .fa-th-large:before { content: "\f009"; } .fa-th:before { content: "\f00a"; } .fa-th-list:before { content: "\f00b"; } .fa-check:before { content: "\f00c"; } .fa-remove:before, .fa-close:before, .fa-times:before { content: "\f00d"; } .fa-search-plus:before { content: "\f00e"; } .fa-search-minus:before { content: "\f010"; } .fa-power-off:before { content: "\f011"; } .fa-signal:before { content: "\f012"; } .fa-gear:before, .fa-cog:before { content: "\f013"; } .fa-trash-o:before { content: "\f014"; } .fa-home:before { content: "\f015"; } .fa-file-o:before { content: "\f016"; } .fa-clock-o:before { content: "\f017"; } .fa-road:before { content: "\f018"; } .fa-download:before { content: "\f019"; } .fa-arrow-circle-o-down:before { content: "\f01a"; } .fa-arrow-circle-o-up:before { content: "\f01b"; } .fa-inbox:before { content: "\f01c"; } .fa-play-circle-o:before { content: "\f01d"; } .fa-rotate-right:before, .fa-repeat:before { content: "\f01e"; } .fa-refresh:before { content: "\f021"; } .fa-list-alt:before { content: "\f022"; } .fa-lock:before { content: "\f023"; } .fa-flag:before { content: "\f024"; } .fa-headphones:before { content: "\f025"; } .fa-volume-off:before { content: "\f026"; } .fa-volume-down:before { content: "\f027"; } .fa-volume-up:before { content: "\f028"; } .fa-qrcode:before { content: "\f029"; } .fa-barcode:before { content: "\f02a"; } .fa-tag:before { content: "\f02b"; } .fa-tags:before { content: "\f02c"; } .fa-book:before { content: "\f02d"; } .fa-bookmark:before { content: "\f02e"; } .fa-print:before { content: "\f02f"; } .fa-camera:before { content: "\f030"; } .fa-font:before { content: "\f031"; } .fa-bold:before { content: "\f032"; } .fa-italic:before { content: "\f033"; } .fa-text-height:before { content: "\f034"; } .fa-text-width:before { content: "\f035"; } .fa-align-left:before { content: "\f036"; } .fa-align-center:before { content: "\f037"; } .fa-align-right:before { content: "\f038"; } .fa-align-justify:before { content: "\f039"; } .fa-list:before { content: "\f03a"; } .fa-dedent:before, .fa-outdent:before { content: "\f03b"; } .fa-indent:before { content: "\f03c"; } .fa-video-camera:before { content: "\f03d"; } .fa-photo:before, .fa-image:before, .fa-picture-o:before { content: "\f03e"; } .fa-pencil:before { content: "\f040"; } .fa-map-marker:before { content: "\f041"; } .fa-adjust:before { content: "\f042"; } .fa-tint:before { content: "\f043"; } .fa-edit:before, .fa-pencil-square-o:before { content: "\f044"; } .fa-share-square-o:before { content: "\f045"; } .fa-check-square-o:before { content: "\f046"; } .fa-arrows:before { content: "\f047"; } .fa-step-backward:before { content: "\f048"; } .fa-fast-backward:before { content: "\f049"; } .fa-backward:before { content: "\f04a"; } .fa-play:before { content: "\f04b"; } .fa-pause:before { content: "\f04c"; } .fa-stop:before { content: "\f04d"; } .fa-forward:before { content: "\f04e"; } .fa-fast-forward:before { content: "\f050"; } .fa-step-forward:before { content: "\f051"; } .fa-eject:before { content: "\f052"; } .fa-chevron-left:before { content: "\f053"; } .fa-chevron-right:before { content: "\f054"; } .fa-plus-circle:before { content: "\f055"; } .fa-minus-circle:before { content: "\f056"; } .fa-times-circle:before { content: "\f057"; } .fa-check-circle:before { content: "\f058"; } .fa-question-circle:before { content: "\f059"; } .fa-info-circle:before { content: "\f05a"; } .fa-crosshairs:before { content: "\f05b"; } .fa-times-circle-o:before { content: "\f05c"; } .fa-check-circle-o:before { content: "\f05d"; } .fa-ban:before { content: "\f05e"; } .fa-arrow-left:before { content: "\f060"; } .fa-arrow-right:before { content: "\f061"; } .fa-arrow-up:before { content: "\f062"; } .fa-arrow-down:before { content: "\f063"; } .fa-mail-forward:before, .fa-share:before { content: "\f064"; } .fa-expand:before { content: "\f065"; } .fa-compress:before { content: "\f066"; } .fa-plus:before { content: "\f067"; } .fa-minus:before { content: "\f068"; } .fa-asterisk:before { content: "\f069"; } .fa-exclamation-circle:before { content: "\f06a"; } .fa-gift:before { content: "\f06b"; } .fa-leaf:before { content: "\f06c"; } .fa-fire:before { content: "\f06d"; } .fa-eye:before { content: "\f06e"; } .fa-eye-slash:before { content: "\f070"; } .fa-warning:before, .fa-exclamation-triangle:before { content: "\f071"; } .fa-plane:before { content: "\f072"; } .fa-calendar:before { content: "\f073"; } .fa-random:before { content: "\f074"; } .fa-comment:before { content: "\f075"; } .fa-magnet:before { content: "\f076"; } .fa-chevron-up:before { content: "\f077"; } .fa-chevron-down:before { content: "\f078"; } .fa-retweet:before { content: "\f079"; } .fa-shopping-cart:before { content: "\f07a"; } .fa-folder:before { content: "\f07b"; } .fa-folder-open:before { content: "\f07c"; } .fa-arrows-v:before { content: "\f07d"; } .fa-arrows-h:before { content: "\f07e"; } .fa-bar-chart-o:before, .fa-bar-chart:before { content: "\f080"; } .fa-twitter-square:before { content: "\f081"; } .fa-facebook-square:before { content: "\f082"; } .fa-camera-retro:before { content: "\f083"; } .fa-key:before { content: "\f084"; } .fa-gears:before, .fa-cogs:before { content: "\f085"; } .fa-comments:before { content: "\f086"; } .fa-thumbs-o-up:before { content: "\f087"; } .fa-thumbs-o-down:before { content: "\f088"; } .fa-star-half:before { content: "\f089"; } .fa-heart-o:before { content: "\f08a"; } .fa-sign-out:before { content: "\f08b"; } .fa-linkedin-square:before { content: "\f08c"; } .fa-thumb-tack:before { content: "\f08d"; } .fa-external-link:before { content: "\f08e"; } .fa-sign-in:before { content: "\f090"; } .fa-trophy:before { content: "\f091"; } .fa-github-square:before { content: "\f092"; } .fa-upload:before { content: "\f093"; } .fa-lemon-o:before { content: "\f094"; } .fa-phone:before { content: "\f095"; } .fa-square-o:before { content: "\f096"; } .fa-bookmark-o:before { content: "\f097"; } .fa-phone-square:before { content: "\f098"; } .fa-twitter:before { content: "\f099"; } .fa-facebook-f:before, .fa-facebook:before { content: "\f09a"; } .fa-github:before { content: "\f09b"; } .fa-unlock:before { content: "\f09c"; } .fa-credit-card:before { content: "\f09d"; } .fa-feed:before, .fa-rss:before { content: "\f09e"; } .fa-hdd-o:before { content: "\f0a0"; } .fa-bullhorn:before { content: "\f0a1"; } .fa-bell:before { content: "\f0f3"; } .fa-certificate:before { content: "\f0a3"; } .fa-hand-o-right:before { content: "\f0a4"; } .fa-hand-o-left:before { content: "\f0a5"; } .fa-hand-o-up:before { content: "\f0a6"; } .fa-hand-o-down:before { content: "\f0a7"; } .fa-arrow-circle-left:before { content: "\f0a8"; } .fa-arrow-circle-right:before { content: "\f0a9"; } .fa-arrow-circle-up:before { content: "\f0aa"; } .fa-arrow-circle-down:before { content: "\f0ab"; } .fa-globe:before { content: "\f0ac"; } .fa-wrench:before { content: "\f0ad"; } .fa-tasks:before { content: "\f0ae"; } .fa-filter:before { content: "\f0b0"; } .fa-briefcase:before { content: "\f0b1"; } .fa-arrows-alt:before { content: "\f0b2"; } .fa-group:before, .fa-users:before { content: "\f0c0"; } .fa-chain:before, .fa-link:before { content: "\f0c1"; } .fa-cloud:before { content: "\f0c2"; } .fa-flask:before { content: "\f0c3"; } .fa-cut:before, .fa-scissors:before { content: "\f0c4"; } .fa-copy:before, .fa-files-o:before { content: "\f0c5"; } .fa-paperclip:before { content: "\f0c6"; } .fa-save:before, .fa-floppy-o:before { content: "\f0c7"; } .fa-square:before { content: "\f0c8"; } .fa-navicon:before, .fa-reorder:before, .fa-bars:before { content: "\f0c9"; } .fa-list-ul:before { content: "\f0ca"; } .fa-list-ol:before { content: "\f0cb"; } .fa-strikethrough:before { content: "\f0cc"; } .fa-underline:before { content: "\f0cd"; } .fa-table:before { content: "\f0ce"; } .fa-magic:before { content: "\f0d0"; } .fa-truck:before { content: "\f0d1"; } .fa-pinterest:before { content: "\f0d2"; } .fa-pinterest-square:before { content: "\f0d3"; } .fa-google-plus-square:before { content: "\f0d4"; } .fa-google-plus:before { content: "\f0d5"; } .fa-money:before { content: "\f0d6"; } .fa-caret-down:before { content: "\f0d7"; } .fa-caret-up:before { content: "\f0d8"; } .fa-caret-left:before { content: "\f0d9"; } .fa-caret-right:before { content: "\f0da"; } .fa-columns:before { content: "\f0db"; } .fa-unsorted:before, .fa-sort:before { content: "\f0dc"; } .fa-sort-down:before, .fa-sort-desc:before { content: "\f0dd"; } .fa-sort-up:before, .fa-sort-asc:before { content: "\f0de"; } .fa-envelope:before { content: "\f0e0"; } .fa-linkedin:before { content: "\f0e1"; } .fa-rotate-left:before, .fa-undo:before { content: "\f0e2"; } .fa-legal:before, .fa-gavel:before { content: "\f0e3"; } .fa-dashboard:before, .fa-tachometer:before { content: "\f0e4"; } .fa-comment-o:before { content: "\f0e5"; } .fa-comments-o:before { content: "\f0e6"; } .fa-flash:before, .fa-bolt:before { content: "\f0e7"; } .fa-sitemap:before { content: "\f0e8"; } .fa-umbrella:before { content: "\f0e9"; } .fa-paste:before, .fa-clipboard:before { content: "\f0ea"; } .fa-lightbulb-o:before { content: "\f0eb"; } .fa-exchange:before { content: "\f0ec"; } .fa-cloud-download:before { content: "\f0ed"; } .fa-cloud-upload:before { content: "\f0ee"; } .fa-user-md:before { content: "\f0f0"; } .fa-stethoscope:before { content: "\f0f1"; } .fa-suitcase:before { content: "\f0f2"; } .fa-bell-o:before { content: "\f0a2"; } .fa-coffee:before { content: "\f0f4"; } .fa-cutlery:before { content: "\f0f5"; } .fa-file-text-o:before { content: "\f0f6"; } .fa-building-o:before { content: "\f0f7"; } .fa-hospital-o:before { content: "\f0f8"; } .fa-ambulance:before { content: "\f0f9"; } .fa-medkit:before { content: "\f0fa"; } .fa-fighter-jet:before { content: "\f0fb"; } .fa-beer:before { content: "\f0fc"; } .fa-h-square:before { content: "\f0fd"; } .fa-plus-square:before { content: "\f0fe"; } .fa-angle-double-left:before { content: "\f100"; } .fa-angle-double-right:before { content: "\f101"; } .fa-angle-double-up:before { content: "\f102"; } .fa-angle-double-down:before { content: "\f103"; } .fa-angle-left:before { content: "\f104"; } .fa-angle-right:before { content: "\f105"; } .fa-angle-up:before { content: "\f106"; } .fa-angle-down:before { content: "\f107"; } .fa-desktop:before { content: "\f108"; } .fa-laptop:before { content: "\f109"; } .fa-tablet:before { content: "\f10a"; } .fa-mobile-phone:before, .fa-mobile:before { content: "\f10b"; } .fa-circle-o:before { content: "\f10c"; } .fa-quote-left:before { content: "\f10d"; } .fa-quote-right:before { content: "\f10e"; } .fa-spinner:before { content: "\f110"; } .fa-circle:before { content: "\f111"; } .fa-mail-reply:before, .fa-reply:before { content: "\f112"; } .fa-github-alt:before { content: "\f113"; } .fa-folder-o:before { content: "\f114"; } .fa-folder-open-o:before { content: "\f115"; } .fa-smile-o:before { content: "\f118"; } .fa-frown-o:before { content: "\f119"; } .fa-meh-o:before { content: "\f11a"; } .fa-gamepad:before { content: "\f11b"; } .fa-keyboard-o:before { content: "\f11c"; } .fa-flag-o:before { content: "\f11d"; } .fa-flag-checkered:before { content: "\f11e"; } .fa-terminal:before { content: "\f120"; } .fa-code:before { content: "\f121"; } .fa-mail-reply-all:before, .fa-reply-all:before { content: "\f122"; } .fa-star-half-empty:before, .fa-star-half-full:before, .fa-star-half-o:before { content: "\f123"; } .fa-location-arrow:before { content: "\f124"; } .fa-crop:before { content: "\f125"; } .fa-code-fork:before { content: "\f126"; } .fa-unlink:before, .fa-chain-broken:before { content: "\f127"; } .fa-question:before { content: "\f128"; } .fa-info:before { content: "\f129"; } .fa-exclamation:before { content: "\f12a"; } .fa-superscript:before { content: "\f12b"; } .fa-subscript:before { content: "\f12c"; } .fa-eraser:before { content: "\f12d"; } .fa-puzzle-piece:before { content: "\f12e"; } .fa-microphone:before { content: "\f130"; } .fa-microphone-slash:before { content: "\f131"; } .fa-shield:before { content: "\f132"; } .fa-calendar-o:before { content: "\f133"; } .fa-fire-extinguisher:before { content: "\f134"; } .fa-rocket:before { content: "\f135"; } .fa-maxcdn:before { content: "\f136"; } .fa-chevron-circle-left:before { content: "\f137"; } .fa-chevron-circle-right:before { content: "\f138"; } .fa-chevron-circle-up:before { content: "\f139"; } .fa-chevron-circle-down:before { content: "\f13a"; } .fa-html5:before { content: "\f13b"; } .fa-css3:before { content: "\f13c"; } .fa-anchor:before { content: "\f13d"; } .fa-unlock-alt:before { content: "\f13e"; } .fa-bullseye:before { content: "\f140"; } .fa-ellipsis-h:before { content: "\f141"; } .fa-ellipsis-v:before { content: "\f142"; } .fa-rss-square:before { content: "\f143"; } .fa-play-circle:before { content: "\f144"; } .fa-ticket:before { content: "\f145"; } .fa-minus-square:before { content: "\f146"; } .fa-minus-square-o:before { content: "\f147"; } .fa-level-up:before { content: "\f148"; } .fa-level-down:before { content: "\f149"; } .fa-check-square:before { content: "\f14a"; } .fa-pencil-square:before { content: "\f14b"; } .fa-external-link-square:before { content: "\f14c"; } .fa-share-square:before { content: "\f14d"; } .fa-compass:before { content: "\f14e"; } .fa-toggle-down:before, .fa-caret-square-o-down:before { content: "\f150"; } .fa-toggle-up:before, .fa-caret-square-o-up:before { content: "\f151"; } .fa-toggle-right:before, .fa-caret-square-o-right:before { content: "\f152"; } .fa-euro:before, .fa-eur:before { content: "\f153"; } .fa-gbp:before { content: "\f154"; } .fa-dollar:before, .fa-usd:before { content: "\f155"; } .fa-rupee:before, .fa-inr:before { content: "\f156"; } .fa-cny:before, .fa-rmb:before, .fa-yen:before, .fa-jpy:before { content: "\f157"; } .fa-ruble:before, .fa-rouble:before, .fa-rub:before { content: "\f158"; } .fa-won:before, .fa-krw:before { content: "\f159"; } .fa-bitcoin:before, .fa-btc:before { content: "\f15a"; } .fa-file:before { content: "\f15b"; } .fa-file-text:before { content: "\f15c"; } .fa-sort-alpha-asc:before { content: "\f15d"; } .fa-sort-alpha-desc:before { content: "\f15e"; } .fa-sort-amount-asc:before { content: "\f160"; } .fa-sort-amount-desc:before { content: "\f161"; } .fa-sort-numeric-asc:before { content: "\f162"; } .fa-sort-numeric-desc:before { content: "\f163"; } .fa-thumbs-up:before { content: "\f164"; } .fa-thumbs-down:before { content: "\f165"; } .fa-youtube-square:before { content: "\f166"; } .fa-youtube:before { content: "\f167"; } .fa-xing:before { content: "\f168"; } .fa-xing-square:before { content: "\f169"; } .fa-youtube-play:before { content: "\f16a"; } .fa-dropbox:before { content: "\f16b"; } .fa-stack-overflow:before { content: "\f16c"; } .fa-instagram:before { content: "\f16d"; } .fa-flickr:before { content: "\f16e"; } .fa-adn:before { content: "\f170"; } .fa-bitbucket:before { content: "\f171"; } .fa-bitbucket-square:before { content: "\f172"; } .fa-tumblr:before { content: "\f173"; } .fa-tumblr-square:before { content: "\f174"; } .fa-long-arrow-down:before { content: "\f175"; } .fa-long-arrow-up:before { content: "\f176"; } .fa-long-arrow-left:before { content: "\f177"; } .fa-long-arrow-right:before { content: "\f178"; } .fa-apple:before { content: "\f179"; } .fa-windows:before { content: "\f17a"; } .fa-android:before { content: "\f17b"; } .fa-linux:before { content: "\f17c"; } .fa-dribbble:before { content: "\f17d"; } .fa-skype:before { content: "\f17e"; } .fa-foursquare:before { content: "\f180"; } .fa-trello:before { content: "\f181"; } .fa-female:before { content: "\f182"; } .fa-male:before { content: "\f183"; } .fa-gittip:before, .fa-gratipay:before { content: "\f184"; } .fa-sun-o:before { content: "\f185"; } .fa-moon-o:before { content: "\f186"; } .fa-archive:before { content: "\f187"; } .fa-bug:before { content: "\f188"; } .fa-vk:before { content: "\f189"; } .fa-weibo:before { content: "\f18a"; } .fa-renren:before { content: "\f18b"; } .fa-pagelines:before { content: "\f18c"; } .fa-stack-exchange:before { content: "\f18d"; } .fa-arrow-circle-o-right:before { content: "\f18e"; } .fa-arrow-circle-o-left:before { content: "\f190"; } .fa-toggle-left:before, .fa-caret-square-o-left:before { content: "\f191"; } .fa-dot-circle-o:before { content: "\f192"; } .fa-wheelchair:before { content: "\f193"; } .fa-vimeo-square:before { content: "\f194"; } .fa-turkish-lira:before, .fa-try:before { content: "\f195"; } .fa-plus-square-o:before { content: "\f196"; } .fa-space-shuttle:before { content: "\f197"; } .fa-slack:before { content: "\f198"; } .fa-envelope-square:before { content: "\f199"; } .fa-wordpress:before { content: "\f19a"; } .fa-openid:before { content: "\f19b"; } .fa-institution:before, .fa-bank:before, .fa-university:before { content: "\f19c"; } .fa-mortar-board:before, .fa-graduation-cap:before { content: "\f19d"; } .fa-yahoo:before { content: "\f19e"; } .fa-google:before { content: "\f1a0"; } .fa-reddit:before { content: "\f1a1"; } .fa-reddit-square:before { content: "\f1a2"; } .fa-stumbleupon-circle:before { content: "\f1a3"; } .fa-stumbleupon:before { content: "\f1a4"; } .fa-delicious:before { content: "\f1a5"; } .fa-digg:before { content: "\f1a6"; } .fa-pied-piper-pp:before { content: "\f1a7"; } .fa-pied-piper-alt:before { content: "\f1a8"; } .fa-drupal:before { content: "\f1a9"; } .fa-joomla:before { content: "\f1aa"; } .fa-language:before { content: "\f1ab"; } .fa-fax:before { content: "\f1ac"; } .fa-building:before { content: "\f1ad"; } .fa-child:before { content: "\f1ae"; } .fa-paw:before { content: "\f1b0"; } .fa-spoon:before { content: "\f1b1"; } .fa-cube:before { content: "\f1b2"; } .fa-cubes:before { content: "\f1b3"; } .fa-behance:before { content: "\f1b4"; } .fa-behance-square:before { content: "\f1b5"; } .fa-steam:before { content: "\f1b6"; } .fa-steam-square:before { content: "\f1b7"; } .fa-recycle:before { content: "\f1b8"; } .fa-automobile:before, .fa-car:before { content: "\f1b9"; } .fa-cab:before, .fa-taxi:before { content: "\f1ba"; } .fa-tree:before { content: "\f1bb"; } .fa-spotify:before { content: "\f1bc"; } .fa-deviantart:before { content: "\f1bd"; } .fa-soundcloud:before { content: "\f1be"; } .fa-database:before { content: "\f1c0"; } .fa-file-pdf-o:before { content: "\f1c1"; } .fa-file-word-o:before { content: "\f1c2"; } .fa-file-excel-o:before { content: "\f1c3"; } .fa-file-powerpoint-o:before { content: "\f1c4"; } .fa-file-photo-o:before, .fa-file-picture-o:before, .fa-file-image-o:before { content: "\f1c5"; } .fa-file-zip-o:before, .fa-file-archive-o:before { content: "\f1c6"; } .fa-file-sound-o:before, .fa-file-audio-o:before { content: "\f1c7"; } .fa-file-movie-o:before, .fa-file-video-o:before { content: "\f1c8"; } .fa-file-code-o:before { content: "\f1c9"; } .fa-vine:before { content: "\f1ca"; } .fa-codepen:before { content: "\f1cb"; } .fa-jsfiddle:before { content: "\f1cc"; } .fa-life-bouy:before, .fa-life-buoy:before, .fa-life-saver:before, .fa-support:before, .fa-life-ring:before { content: "\f1cd"; } .fa-circle-o-notch:before { content: "\f1ce"; } .fa-ra:before, .fa-resistance:before, .fa-rebel:before { content: "\f1d0"; } .fa-ge:before, .fa-empire:before { content: "\f1d1"; } .fa-git-square:before { content: "\f1d2"; } .fa-git:before { content: "\f1d3"; } .fa-y-combinator-square:before, .fa-yc-square:before, .fa-hacker-news:before { content: "\f1d4"; } .fa-tencent-weibo:before { content: "\f1d5"; } .fa-qq:before { content: "\f1d6"; } .fa-wechat:before, .fa-weixin:before { content: "\f1d7"; } .fa-send:before, .fa-paper-plane:before { content: "\f1d8"; } .fa-send-o:before, .fa-paper-plane-o:before { content: "\f1d9"; } .fa-history:before { content: "\f1da"; } .fa-circle-thin:before { content: "\f1db"; } .fa-header:before { content: "\f1dc"; } .fa-paragraph:before { content: "\f1dd"; } .fa-sliders:before { content: "\f1de"; } .fa-share-alt:before { content: "\f1e0"; } .fa-share-alt-square:before { content: "\f1e1"; } .fa-bomb:before { content: "\f1e2"; } .fa-soccer-ball-o:before, .fa-futbol-o:before { content: "\f1e3"; } .fa-tty:before { content: "\f1e4"; } .fa-binoculars:before { content: "\f1e5"; } .fa-plug:before { content: "\f1e6"; } .fa-slideshare:before { content: "\f1e7"; } .fa-twitch:before { content: "\f1e8"; } .fa-yelp:before { content: "\f1e9"; } .fa-newspaper-o:before { content: "\f1ea"; } .fa-wifi:before { content: "\f1eb"; } .fa-calculator:before { content: "\f1ec"; } .fa-paypal:before { content: "\f1ed"; } .fa-google-wallet:before { content: "\f1ee"; } .fa-cc-visa:before { content: "\f1f0"; } .fa-cc-mastercard:before { content: "\f1f1"; } .fa-cc-discover:before { content: "\f1f2"; } .fa-cc-amex:before { content: "\f1f3"; } .fa-cc-paypal:before { content: "\f1f4"; } .fa-cc-stripe:before { content: "\f1f5"; } .fa-bell-slash:before { content: "\f1f6"; } .fa-bell-slash-o:before { content: "\f1f7"; } .fa-trash:before { content: "\f1f8"; } .fa-copyright:before { content: "\f1f9"; } .fa-at:before { content: "\f1fa"; } .fa-eyedropper:before { content: "\f1fb"; } .fa-paint-brush:before { content: "\f1fc"; } .fa-birthday-cake:before { content: "\f1fd"; } .fa-area-chart:before { content: "\f1fe"; } .fa-pie-chart:before { content: "\f200"; } .fa-line-chart:before { content: "\f201"; } .fa-lastfm:before { content: "\f202"; } .fa-lastfm-square:before { content: "\f203"; } .fa-toggle-off:before { content: "\f204"; } .fa-toggle-on:before { content: "\f205"; } .fa-bicycle:before { content: "\f206"; } .fa-bus:before { content: "\f207"; } .fa-ioxhost:before { content: "\f208"; } .fa-angellist:before { content: "\f209"; } .fa-cc:before { content: "\f20a"; } .fa-shekel:before, .fa-sheqel:before, .fa-ils:before { content: "\f20b"; } .fa-meanpath:before { content: "\f20c"; } .fa-buysellads:before { content: "\f20d"; } .fa-connectdevelop:before { content: "\f20e"; } .fa-dashcube:before { content: "\f210"; } .fa-forumbee:before { content: "\f211"; } .fa-leanpub:before { content: "\f212"; } .fa-sellsy:before { content: "\f213"; } .fa-shirtsinbulk:before { content: "\f214"; } .fa-simplybuilt:before { content: "\f215"; } .fa-skyatlas:before { content: "\f216"; } .fa-cart-plus:before { content: "\f217"; } .fa-cart-arrow-down:before { content: "\f218"; } .fa-diamond:before { content: "\f219"; } .fa-ship:before { content: "\f21a"; } .fa-user-secret:before { content: "\f21b"; } .fa-motorcycle:before { content: "\f21c"; } .fa-street-view:before { content: "\f21d"; } .fa-heartbeat:before { content: "\f21e"; } .fa-venus:before { content: "\f221"; } .fa-mars:before { content: "\f222"; } .fa-mercury:before { content: "\f223"; } .fa-intersex:before, .fa-transgender:before { content: "\f224"; } .fa-transgender-alt:before { content: "\f225"; } .fa-venus-double:before { content: "\f226"; } .fa-mars-double:before { content: "\f227"; } .fa-venus-mars:before { content: "\f228"; } .fa-mars-stroke:before { content: "\f229"; } .fa-mars-stroke-v:before { content: "\f22a"; } .fa-mars-stroke-h:before { content: "\f22b"; } .fa-neuter:before { content: "\f22c"; } .fa-genderless:before { content: "\f22d"; } .fa-facebook-official:before { content: "\f230"; } .fa-pinterest-p:before { content: "\f231"; } .fa-whatsapp:before { content: "\f232"; } .fa-server:before { content: "\f233"; } .fa-user-plus:before { content: "\f234"; } .fa-user-times:before { content: "\f235"; } .fa-hotel:before, .fa-bed:before { content: "\f236"; } .fa-viacoin:before { content: "\f237"; } .fa-train:before { content: "\f238"; } .fa-subway:before { content: "\f239"; } .fa-medium:before { content: "\f23a"; } .fa-yc:before, .fa-y-combinator:before { content: "\f23b"; } .fa-optin-monster:before { content: "\f23c"; } .fa-opencart:before { content: "\f23d"; } .fa-expeditedssl:before { content: "\f23e"; } .fa-battery-4:before, .fa-battery:before, .fa-battery-full:before { content: "\f240"; } .fa-battery-3:before, .fa-battery-three-quarters:before { content: "\f241"; } .fa-battery-2:before, .fa-battery-half:before { content: "\f242"; } .fa-battery-1:before, .fa-battery-quarter:before { content: "\f243"; } .fa-battery-0:before, .fa-battery-empty:before { content: "\f244"; } .fa-mouse-pointer:before { content: "\f245"; } .fa-i-cursor:before { content: "\f246"; } .fa-object-group:before { content: "\f247"; } .fa-object-ungroup:before { content: "\f248"; } .fa-sticky-note:before { content: "\f249"; } .fa-sticky-note-o:before { content: "\f24a"; } .fa-cc-jcb:before { content: "\f24b"; } .fa-cc-diners-club:before { content: "\f24c"; } .fa-clone:before { content: "\f24d"; } .fa-balance-scale:before { content: "\f24e"; } .fa-hourglass-o:before { content: "\f250"; } .fa-hourglass-1:before, .fa-hourglass-start:before { content: "\f251"; } .fa-hourglass-2:before, .fa-hourglass-half:before { content: "\f252"; } .fa-hourglass-3:before, .fa-hourglass-end:before { content: "\f253"; } .fa-hourglass:before { content: "\f254"; } .fa-hand-grab-o:before, .fa-hand-rock-o:before { content: "\f255"; } .fa-hand-stop-o:before, .fa-hand-paper-o:before { content: "\f256"; } .fa-hand-scissors-o:before { content: "\f257"; } .fa-hand-lizard-o:before { content: "\f258"; } .fa-hand-spock-o:before { content: "\f259"; } .fa-hand-pointer-o:before { content: "\f25a"; } .fa-hand-peace-o:before { content: "\f25b"; } .fa-trademark:before { content: "\f25c"; } .fa-registered:before { content: "\f25d"; } .fa-creative-commons:before { content: "\f25e"; } .fa-gg:before { content: "\f260"; } .fa-gg-circle:before { content: "\f261"; } .fa-tripadvisor:before { content: "\f262"; } .fa-odnoklassniki:before { content: "\f263"; } .fa-odnoklassniki-square:before { content: "\f264"; } .fa-get-pocket:before { content: "\f265"; } .fa-wikipedia-w:before { content: "\f266"; } .fa-safari:before { content: "\f267"; } .fa-chrome:before { content: "\f268"; } .fa-firefox:before { content: "\f269"; } .fa-opera:before { content: "\f26a"; } .fa-internet-explorer:before { content: "\f26b"; } .fa-tv:before, .fa-television:before { content: "\f26c"; } .fa-contao:before { content: "\f26d"; } .fa-500px:before { content: "\f26e"; } .fa-amazon:before { content: "\f270"; } .fa-calendar-plus-o:before { content: "\f271"; } .fa-calendar-minus-o:before { content: "\f272"; } .fa-calendar-times-o:before { content: "\f273"; } .fa-calendar-check-o:before { content: "\f274"; } .fa-industry:before { content: "\f275"; } .fa-map-pin:before { content: "\f276"; } .fa-map-signs:before { content: "\f277"; } .fa-map-o:before { content: "\f278"; } .fa-map:before { content: "\f279"; } .fa-commenting:before { content: "\f27a"; } .fa-commenting-o:before { content: "\f27b"; } .fa-houzz:before { content: "\f27c"; } .fa-vimeo:before { content: "\f27d"; } .fa-black-tie:before { content: "\f27e"; } .fa-fonticons:before { content: "\f280"; } .fa-reddit-alien:before { content: "\f281"; } .fa-edge:before { content: "\f282"; } .fa-credit-card-alt:before { content: "\f283"; } .fa-codiepie:before { content: "\f284"; } .fa-modx:before { content: "\f285"; } .fa-fort-awesome:before { content: "\f286"; } .fa-usb:before { content: "\f287"; } .fa-product-hunt:before { content: "\f288"; } .fa-mixcloud:before { content: "\f289"; } .fa-scribd:before { content: "\f28a"; } .fa-pause-circle:before { content: "\f28b"; } .fa-pause-circle-o:before { content: "\f28c"; } .fa-stop-circle:before { content: "\f28d"; } .fa-stop-circle-o:before { content: "\f28e"; } .fa-shopping-bag:before { content: "\f290"; } .fa-shopping-basket:before { content: "\f291"; } .fa-hashtag:before { content: "\f292"; } .fa-bluetooth:before { content: "\f293"; } .fa-bluetooth-b:before { content: "\f294"; } .fa-percent:before { content: "\f295"; } .fa-gitlab:before { content: "\f296"; } .fa-wpbeginner:before { content: "\f297"; } .fa-wpforms:before { content: "\f298"; } .fa-envira:before { content: "\f299"; } .fa-universal-access:before { content: "\f29a"; } .fa-wheelchair-alt:before { content: "\f29b"; } .fa-question-circle-o:before { content: "\f29c"; } .fa-blind:before { content: "\f29d"; } .fa-audio-description:before { content: "\f29e"; } .fa-volume-control-phone:before { content: "\f2a0"; } .fa-braille:before { content: "\f2a1"; } .fa-assistive-listening-systems:before { content: "\f2a2"; } .fa-asl-interpreting:before, .fa-american-sign-language-interpreting:before { content: "\f2a3"; } .fa-deafness:before, .fa-hard-of-hearing:before, .fa-deaf:before { content: "\f2a4"; } .fa-glide:before { content: "\f2a5"; } .fa-glide-g:before { content: "\f2a6"; } .fa-signing:before, .fa-sign-language:before { content: "\f2a7"; } .fa-low-vision:before { content: "\f2a8"; } .fa-viadeo:before { content: "\f2a9"; } .fa-viadeo-square:before { content: "\f2aa"; } .fa-snapchat:before { content: "\f2ab"; } .fa-snapchat-ghost:before { content: "\f2ac"; } .fa-snapchat-square:before { content: "\f2ad"; } .fa-pied-piper:before { content: "\f2ae"; } .fa-first-order:before { content: "\f2b0"; } .fa-yoast:before { content: "\f2b1"; } .fa-themeisle:before { content: "\f2b2"; } .fa-google-plus-circle:before, .fa-google-plus-official:before { content: "\f2b3"; } .fa-fa:before, .fa-font-awesome:before { content: "\f2b4"; } .fa-handshake-o:before { content: "\f2b5"; } .fa-envelope-open:before { content: "\f2b6"; } .fa-envelope-open-o:before { content: "\f2b7"; } .fa-linode:before { content: "\f2b8"; } .fa-address-book:before { content: "\f2b9"; } .fa-address-book-o:before { content: "\f2ba"; } .fa-vcard:before, .fa-address-card:before { content: "\f2bb"; } .fa-vcard-o:before, .fa-address-card-o:before { content: "\f2bc"; } .fa-user-circle:before { content: "\f2bd"; } .fa-user-circle-o:before { content: "\f2be"; } .fa-user-o:before { content: "\f2c0"; } .fa-id-badge:before { content: "\f2c1"; } .fa-drivers-license:before, .fa-id-card:before { content: "\f2c2"; } .fa-drivers-license-o:before, .fa-id-card-o:before { content: "\f2c3"; } .fa-quora:before { content: "\f2c4"; } .fa-free-code-camp:before { content: "\f2c5"; } .fa-telegram:before { content: "\f2c6"; } .fa-thermometer-4:before, .fa-thermometer:before, .fa-thermometer-full:before { content: "\f2c7"; } .fa-thermometer-3:before, .fa-thermometer-three-quarters:before { content: "\f2c8"; } .fa-thermometer-2:before, .fa-thermometer-half:before { content: "\f2c9"; } .fa-thermometer-1:before, .fa-thermometer-quarter:before { content: "\f2ca"; } .fa-thermometer-0:before, .fa-thermometer-empty:before { content: "\f2cb"; } .fa-shower:before { content: "\f2cc"; } .fa-bathtub:before, .fa-s15:before, .fa-bath:before { content: "\f2cd"; } .fa-podcast:before { content: "\f2ce"; } .fa-window-maximize:before { content: "\f2d0"; } .fa-window-minimize:before { content: "\f2d1"; } .fa-window-restore:before { content: "\f2d2"; } .fa-times-rectangle:before, .fa-window-close:before { content: "\f2d3"; } .fa-times-rectangle-o:before, .fa-window-close-o:before { content: "\f2d4"; } .fa-bandcamp:before { content: "\f2d5"; } .fa-grav:before { content: "\f2d6"; } .fa-etsy:before { content: "\f2d7"; } .fa-imdb:before { content: "\f2d8"; } .fa-ravelry:before { content: "\f2d9"; } .fa-eercast:before { content: "\f2da"; } .fa-microchip:before { content: "\f2db"; } .fa-snowflake-o:before { content: "\f2dc"; } .fa-superpowers:before { content: "\f2dd"; } .fa-wpexplorer:before { content: "\f2de"; } .fa-meetup:before { content: "\f2e0"; } .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; } .sr-only-focusable:active, .sr-only-focusable:focus { position: static; width: auto; height: auto; margin: 0; overflow: visible; clip: auto; } ================================================ FILE: static/font-awesome-4.7.0/less/animated.less ================================================ // Animated Icons // -------------------------- .@{fa-css-prefix}-spin { -webkit-animation: fa-spin 2s infinite linear; animation: fa-spin 2s infinite linear; } .@{fa-css-prefix}-pulse { -webkit-animation: fa-spin 1s infinite steps(8); animation: fa-spin 1s infinite steps(8); } @-webkit-keyframes fa-spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg); } } @keyframes fa-spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg); } } ================================================ FILE: static/font-awesome-4.7.0/less/bordered-pulled.less ================================================ // Bordered & Pulled // ------------------------- .@{fa-css-prefix}-border { padding: .2em .25em .15em; border: solid .08em @fa-border-color; border-radius: .1em; } .@{fa-css-prefix}-pull-left { float: left; } .@{fa-css-prefix}-pull-right { float: right; } .@{fa-css-prefix} { &.@{fa-css-prefix}-pull-left { margin-right: .3em; } &.@{fa-css-prefix}-pull-right { margin-left: .3em; } } /* Deprecated as of 4.4.0 */ .pull-right { float: right; } .pull-left { float: left; } .@{fa-css-prefix} { &.pull-left { margin-right: .3em; } &.pull-right { margin-left: .3em; } } ================================================ FILE: static/font-awesome-4.7.0/less/core.less ================================================ // Base Class Definition // ------------------------- .@{fa-css-prefix} { display: inline-block; font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration font-size: inherit; // can't have font-size inherit on line above, so need to override text-rendering: auto; // optimizelegibility throws things off #1094 -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } ================================================ FILE: static/font-awesome-4.7.0/less/fixed-width.less ================================================ // Fixed Width Icons // ------------------------- .@{fa-css-prefix}-fw { width: (18em / 14); text-align: center; } ================================================ FILE: static/font-awesome-4.7.0/less/font-awesome.less ================================================ /*! * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) */ @import "variables.less"; @import "mixins.less"; @import "path.less"; @import "core.less"; @import "larger.less"; @import "fixed-width.less"; @import "list.less"; @import "bordered-pulled.less"; @import "animated.less"; @import "rotated-flipped.less"; @import "stacked.less"; @import "icons.less"; @import "screen-reader.less"; ================================================ FILE: static/font-awesome-4.7.0/less/icons.less ================================================ /* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen readers do not read off random characters that represent icons */ .@{fa-css-prefix}-glass:before { content: @fa-var-glass; } .@{fa-css-prefix}-music:before { content: @fa-var-music; } .@{fa-css-prefix}-search:before { content: @fa-var-search; } .@{fa-css-prefix}-envelope-o:before { content: @fa-var-envelope-o; } .@{fa-css-prefix}-heart:before { content: @fa-var-heart; } .@{fa-css-prefix}-star:before { content: @fa-var-star; } .@{fa-css-prefix}-star-o:before { content: @fa-var-star-o; } .@{fa-css-prefix}-user:before { content: @fa-var-user; } .@{fa-css-prefix}-film:before { content: @fa-var-film; } .@{fa-css-prefix}-th-large:before { content: @fa-var-th-large; } .@{fa-css-prefix}-th:before { content: @fa-var-th; } .@{fa-css-prefix}-th-list:before { content: @fa-var-th-list; } .@{fa-css-prefix}-check:before { content: @fa-var-check; } .@{fa-css-prefix}-remove:before, .@{fa-css-prefix}-close:before, .@{fa-css-prefix}-times:before { content: @fa-var-times; } .@{fa-css-prefix}-search-plus:before { content: @fa-var-search-plus; } .@{fa-css-prefix}-search-minus:before { content: @fa-var-search-minus; } .@{fa-css-prefix}-power-off:before { content: @fa-var-power-off; } .@{fa-css-prefix}-signal:before { content: @fa-var-signal; } .@{fa-css-prefix}-gear:before, .@{fa-css-prefix}-cog:before { content: @fa-var-cog; } .@{fa-css-prefix}-trash-o:before { content: @fa-var-trash-o; } .@{fa-css-prefix}-home:before { content: @fa-var-home; } .@{fa-css-prefix}-file-o:before { content: @fa-var-file-o; } .@{fa-css-prefix}-clock-o:before { content: @fa-var-clock-o; } .@{fa-css-prefix}-road:before { content: @fa-var-road; } .@{fa-css-prefix}-download:before { content: @fa-var-download; } .@{fa-css-prefix}-arrow-circle-o-down:before { content: @fa-var-arrow-circle-o-down; } .@{fa-css-prefix}-arrow-circle-o-up:before { content: @fa-var-arrow-circle-o-up; } .@{fa-css-prefix}-inbox:before { content: @fa-var-inbox; } .@{fa-css-prefix}-play-circle-o:before { content: @fa-var-play-circle-o; } .@{fa-css-prefix}-rotate-right:before, .@{fa-css-prefix}-repeat:before { content: @fa-var-repeat; } .@{fa-css-prefix}-refresh:before { content: @fa-var-refresh; } .@{fa-css-prefix}-list-alt:before { content: @fa-var-list-alt; } .@{fa-css-prefix}-lock:before { content: @fa-var-lock; } .@{fa-css-prefix}-flag:before { content: @fa-var-flag; } .@{fa-css-prefix}-headphones:before { content: @fa-var-headphones; } .@{fa-css-prefix}-volume-off:before { content: @fa-var-volume-off; } .@{fa-css-prefix}-volume-down:before { content: @fa-var-volume-down; } .@{fa-css-prefix}-volume-up:before { content: @fa-var-volume-up; } .@{fa-css-prefix}-qrcode:before { content: @fa-var-qrcode; } .@{fa-css-prefix}-barcode:before { content: @fa-var-barcode; } .@{fa-css-prefix}-tag:before { content: @fa-var-tag; } .@{fa-css-prefix}-tags:before { content: @fa-var-tags; } .@{fa-css-prefix}-book:before { content: @fa-var-book; } .@{fa-css-prefix}-bookmark:before { content: @fa-var-bookmark; } .@{fa-css-prefix}-print:before { content: @fa-var-print; } .@{fa-css-prefix}-camera:before { content: @fa-var-camera; } .@{fa-css-prefix}-font:before { content: @fa-var-font; } .@{fa-css-prefix}-bold:before { content: @fa-var-bold; } .@{fa-css-prefix}-italic:before { content: @fa-var-italic; } .@{fa-css-prefix}-text-height:before { content: @fa-var-text-height; } .@{fa-css-prefix}-text-width:before { content: @fa-var-text-width; } .@{fa-css-prefix}-align-left:before { content: @fa-var-align-left; } .@{fa-css-prefix}-align-center:before { content: @fa-var-align-center; } .@{fa-css-prefix}-align-right:before { content: @fa-var-align-right; } .@{fa-css-prefix}-align-justify:before { content: @fa-var-align-justify; } .@{fa-css-prefix}-list:before { content: @fa-var-list; } .@{fa-css-prefix}-dedent:before, .@{fa-css-prefix}-outdent:before { content: @fa-var-outdent; } .@{fa-css-prefix}-indent:before { content: @fa-var-indent; } .@{fa-css-prefix}-video-camera:before { content: @fa-var-video-camera; } .@{fa-css-prefix}-photo:before, .@{fa-css-prefix}-image:before, .@{fa-css-prefix}-picture-o:before { content: @fa-var-picture-o; } .@{fa-css-prefix}-pencil:before { content: @fa-var-pencil; } .@{fa-css-prefix}-map-marker:before { content: @fa-var-map-marker; } .@{fa-css-prefix}-adjust:before { content: @fa-var-adjust; } .@{fa-css-prefix}-tint:before { content: @fa-var-tint; } .@{fa-css-prefix}-edit:before, .@{fa-css-prefix}-pencil-square-o:before { content: @fa-var-pencil-square-o; } .@{fa-css-prefix}-share-square-o:before { content: @fa-var-share-square-o; } .@{fa-css-prefix}-check-square-o:before { content: @fa-var-check-square-o; } .@{fa-css-prefix}-arrows:before { content: @fa-var-arrows; } .@{fa-css-prefix}-step-backward:before { content: @fa-var-step-backward; } .@{fa-css-prefix}-fast-backward:before { content: @fa-var-fast-backward; } .@{fa-css-prefix}-backward:before { content: @fa-var-backward; } .@{fa-css-prefix}-play:before { content: @fa-var-play; } .@{fa-css-prefix}-pause:before { content: @fa-var-pause; } .@{fa-css-prefix}-stop:before { content: @fa-var-stop; } .@{fa-css-prefix}-forward:before { content: @fa-var-forward; } .@{fa-css-prefix}-fast-forward:before { content: @fa-var-fast-forward; } .@{fa-css-prefix}-step-forward:before { content: @fa-var-step-forward; } .@{fa-css-prefix}-eject:before { content: @fa-var-eject; } .@{fa-css-prefix}-chevron-left:before { content: @fa-var-chevron-left; } .@{fa-css-prefix}-chevron-right:before { content: @fa-var-chevron-right; } .@{fa-css-prefix}-plus-circle:before { content: @fa-var-plus-circle; } .@{fa-css-prefix}-minus-circle:before { content: @fa-var-minus-circle; } .@{fa-css-prefix}-times-circle:before { content: @fa-var-times-circle; } .@{fa-css-prefix}-check-circle:before { content: @fa-var-check-circle; } .@{fa-css-prefix}-question-circle:before { content: @fa-var-question-circle; } .@{fa-css-prefix}-info-circle:before { content: @fa-var-info-circle; } .@{fa-css-prefix}-crosshairs:before { content: @fa-var-crosshairs; } .@{fa-css-prefix}-times-circle-o:before { content: @fa-var-times-circle-o; } .@{fa-css-prefix}-check-circle-o:before { content: @fa-var-check-circle-o; } .@{fa-css-prefix}-ban:before { content: @fa-var-ban; } .@{fa-css-prefix}-arrow-left:before { content: @fa-var-arrow-left; } .@{fa-css-prefix}-arrow-right:before { content: @fa-var-arrow-right; } .@{fa-css-prefix}-arrow-up:before { content: @fa-var-arrow-up; } .@{fa-css-prefix}-arrow-down:before { content: @fa-var-arrow-down; } .@{fa-css-prefix}-mail-forward:before, .@{fa-css-prefix}-share:before { content: @fa-var-share; } .@{fa-css-prefix}-expand:before { content: @fa-var-expand; } .@{fa-css-prefix}-compress:before { content: @fa-var-compress; } .@{fa-css-prefix}-plus:before { content: @fa-var-plus; } .@{fa-css-prefix}-minus:before { content: @fa-var-minus; } .@{fa-css-prefix}-asterisk:before { content: @fa-var-asterisk; } .@{fa-css-prefix}-exclamation-circle:before { content: @fa-var-exclamation-circle; } .@{fa-css-prefix}-gift:before { content: @fa-var-gift; } .@{fa-css-prefix}-leaf:before { content: @fa-var-leaf; } .@{fa-css-prefix}-fire:before { content: @fa-var-fire; } .@{fa-css-prefix}-eye:before { content: @fa-var-eye; } .@{fa-css-prefix}-eye-slash:before { content: @fa-var-eye-slash; } .@{fa-css-prefix}-warning:before, .@{fa-css-prefix}-exclamation-triangle:before { content: @fa-var-exclamation-triangle; } .@{fa-css-prefix}-plane:before { content: @fa-var-plane; } .@{fa-css-prefix}-calendar:before { content: @fa-var-calendar; } .@{fa-css-prefix}-random:before { content: @fa-var-random; } .@{fa-css-prefix}-comment:before { content: @fa-var-comment; } .@{fa-css-prefix}-magnet:before { content: @fa-var-magnet; } .@{fa-css-prefix}-chevron-up:before { content: @fa-var-chevron-up; } .@{fa-css-prefix}-chevron-down:before { content: @fa-var-chevron-down; } .@{fa-css-prefix}-retweet:before { content: @fa-var-retweet; } .@{fa-css-prefix}-shopping-cart:before { content: @fa-var-shopping-cart; } .@{fa-css-prefix}-folder:before { content: @fa-var-folder; } .@{fa-css-prefix}-folder-open:before { content: @fa-var-folder-open; } .@{fa-css-prefix}-arrows-v:before { content: @fa-var-arrows-v; } .@{fa-css-prefix}-arrows-h:before { content: @fa-var-arrows-h; } .@{fa-css-prefix}-bar-chart-o:before, .@{fa-css-prefix}-bar-chart:before { content: @fa-var-bar-chart; } .@{fa-css-prefix}-twitter-square:before { content: @fa-var-twitter-square; } .@{fa-css-prefix}-facebook-square:before { content: @fa-var-facebook-square; } .@{fa-css-prefix}-camera-retro:before { content: @fa-var-camera-retro; } .@{fa-css-prefix}-key:before { content: @fa-var-key; } .@{fa-css-prefix}-gears:before, .@{fa-css-prefix}-cogs:before { content: @fa-var-cogs; } .@{fa-css-prefix}-comments:before { content: @fa-var-comments; } .@{fa-css-prefix}-thumbs-o-up:before { content: @fa-var-thumbs-o-up; } .@{fa-css-prefix}-thumbs-o-down:before { content: @fa-var-thumbs-o-down; } .@{fa-css-prefix}-star-half:before { content: @fa-var-star-half; } .@{fa-css-prefix}-heart-o:before { content: @fa-var-heart-o; } .@{fa-css-prefix}-sign-out:before { content: @fa-var-sign-out; } .@{fa-css-prefix}-linkedin-square:before { content: @fa-var-linkedin-square; } .@{fa-css-prefix}-thumb-tack:before { content: @fa-var-thumb-tack; } .@{fa-css-prefix}-external-link:before { content: @fa-var-external-link; } .@{fa-css-prefix}-sign-in:before { content: @fa-var-sign-in; } .@{fa-css-prefix}-trophy:before { content: @fa-var-trophy; } .@{fa-css-prefix}-github-square:before { content: @fa-var-github-square; } .@{fa-css-prefix}-upload:before { content: @fa-var-upload; } .@{fa-css-prefix}-lemon-o:before { content: @fa-var-lemon-o; } .@{fa-css-prefix}-phone:before { content: @fa-var-phone; } .@{fa-css-prefix}-square-o:before { content: @fa-var-square-o; } .@{fa-css-prefix}-bookmark-o:before { content: @fa-var-bookmark-o; } .@{fa-css-prefix}-phone-square:before { content: @fa-var-phone-square; } .@{fa-css-prefix}-twitter:before { content: @fa-var-twitter; } .@{fa-css-prefix}-facebook-f:before, .@{fa-css-prefix}-facebook:before { content: @fa-var-facebook; } .@{fa-css-prefix}-github:before { content: @fa-var-github; } .@{fa-css-prefix}-unlock:before { content: @fa-var-unlock; } .@{fa-css-prefix}-credit-card:before { content: @fa-var-credit-card; } .@{fa-css-prefix}-feed:before, .@{fa-css-prefix}-rss:before { content: @fa-var-rss; } .@{fa-css-prefix}-hdd-o:before { content: @fa-var-hdd-o; } .@{fa-css-prefix}-bullhorn:before { content: @fa-var-bullhorn; } .@{fa-css-prefix}-bell:before { content: @fa-var-bell; } .@{fa-css-prefix}-certificate:before { content: @fa-var-certificate; } .@{fa-css-prefix}-hand-o-right:before { content: @fa-var-hand-o-right; } .@{fa-css-prefix}-hand-o-left:before { content: @fa-var-hand-o-left; } .@{fa-css-prefix}-hand-o-up:before { content: @fa-var-hand-o-up; } .@{fa-css-prefix}-hand-o-down:before { content: @fa-var-hand-o-down; } .@{fa-css-prefix}-arrow-circle-left:before { content: @fa-var-arrow-circle-left; } .@{fa-css-prefix}-arrow-circle-right:before { content: @fa-var-arrow-circle-right; } .@{fa-css-prefix}-arrow-circle-up:before { content: @fa-var-arrow-circle-up; } .@{fa-css-prefix}-arrow-circle-down:before { content: @fa-var-arrow-circle-down; } .@{fa-css-prefix}-globe:before { content: @fa-var-globe; } .@{fa-css-prefix}-wrench:before { content: @fa-var-wrench; } .@{fa-css-prefix}-tasks:before { content: @fa-var-tasks; } .@{fa-css-prefix}-filter:before { content: @fa-var-filter; } .@{fa-css-prefix}-briefcase:before { content: @fa-var-briefcase; } .@{fa-css-prefix}-arrows-alt:before { content: @fa-var-arrows-alt; } .@{fa-css-prefix}-group:before, .@{fa-css-prefix}-users:before { content: @fa-var-users; } .@{fa-css-prefix}-chain:before, .@{fa-css-prefix}-link:before { content: @fa-var-link; } .@{fa-css-prefix}-cloud:before { content: @fa-var-cloud; } .@{fa-css-prefix}-flask:before { content: @fa-var-flask; } .@{fa-css-prefix}-cut:before, .@{fa-css-prefix}-scissors:before { content: @fa-var-scissors; } .@{fa-css-prefix}-copy:before, .@{fa-css-prefix}-files-o:before { content: @fa-var-files-o; } .@{fa-css-prefix}-paperclip:before { content: @fa-var-paperclip; } .@{fa-css-prefix}-save:before, .@{fa-css-prefix}-floppy-o:before { content: @fa-var-floppy-o; } .@{fa-css-prefix}-square:before { content: @fa-var-square; } .@{fa-css-prefix}-navicon:before, .@{fa-css-prefix}-reorder:before, .@{fa-css-prefix}-bars:before { content: @fa-var-bars; } .@{fa-css-prefix}-list-ul:before { content: @fa-var-list-ul; } .@{fa-css-prefix}-list-ol:before { content: @fa-var-list-ol; } .@{fa-css-prefix}-strikethrough:before { content: @fa-var-strikethrough; } .@{fa-css-prefix}-underline:before { content: @fa-var-underline; } .@{fa-css-prefix}-table:before { content: @fa-var-table; } .@{fa-css-prefix}-magic:before { content: @fa-var-magic; } .@{fa-css-prefix}-truck:before { content: @fa-var-truck; } .@{fa-css-prefix}-pinterest:before { content: @fa-var-pinterest; } .@{fa-css-prefix}-pinterest-square:before { content: @fa-var-pinterest-square; } .@{fa-css-prefix}-google-plus-square:before { content: @fa-var-google-plus-square; } .@{fa-css-prefix}-google-plus:before { content: @fa-var-google-plus; } .@{fa-css-prefix}-money:before { content: @fa-var-money; } .@{fa-css-prefix}-caret-down:before { content: @fa-var-caret-down; } .@{fa-css-prefix}-caret-up:before { content: @fa-var-caret-up; } .@{fa-css-prefix}-caret-left:before { content: @fa-var-caret-left; } .@{fa-css-prefix}-caret-right:before { content: @fa-var-caret-right; } .@{fa-css-prefix}-columns:before { content: @fa-var-columns; } .@{fa-css-prefix}-unsorted:before, .@{fa-css-prefix}-sort:before { content: @fa-var-sort; } .@{fa-css-prefix}-sort-down:before, .@{fa-css-prefix}-sort-desc:before { content: @fa-var-sort-desc; } .@{fa-css-prefix}-sort-up:before, .@{fa-css-prefix}-sort-asc:before { content: @fa-var-sort-asc; } .@{fa-css-prefix}-envelope:before { content: @fa-var-envelope; } .@{fa-css-prefix}-linkedin:before { content: @fa-var-linkedin; } .@{fa-css-prefix}-rotate-left:before, .@{fa-css-prefix}-undo:before { content: @fa-var-undo; } .@{fa-css-prefix}-legal:before, .@{fa-css-prefix}-gavel:before { content: @fa-var-gavel; } .@{fa-css-prefix}-dashboard:before, .@{fa-css-prefix}-tachometer:before { content: @fa-var-tachometer; } .@{fa-css-prefix}-comment-o:before { content: @fa-var-comment-o; } .@{fa-css-prefix}-comments-o:before { content: @fa-var-comments-o; } .@{fa-css-prefix}-flash:before, .@{fa-css-prefix}-bolt:before { content: @fa-var-bolt; } .@{fa-css-prefix}-sitemap:before { content: @fa-var-sitemap; } .@{fa-css-prefix}-umbrella:before { content: @fa-var-umbrella; } .@{fa-css-prefix}-paste:before, .@{fa-css-prefix}-clipboard:before { content: @fa-var-clipboard; } .@{fa-css-prefix}-lightbulb-o:before { content: @fa-var-lightbulb-o; } .@{fa-css-prefix}-exchange:before { content: @fa-var-exchange; } .@{fa-css-prefix}-cloud-download:before { content: @fa-var-cloud-download; } .@{fa-css-prefix}-cloud-upload:before { content: @fa-var-cloud-upload; } .@{fa-css-prefix}-user-md:before { content: @fa-var-user-md; } .@{fa-css-prefix}-stethoscope:before { content: @fa-var-stethoscope; } .@{fa-css-prefix}-suitcase:before { content: @fa-var-suitcase; } .@{fa-css-prefix}-bell-o:before { content: @fa-var-bell-o; } .@{fa-css-prefix}-coffee:before { content: @fa-var-coffee; } .@{fa-css-prefix}-cutlery:before { content: @fa-var-cutlery; } .@{fa-css-prefix}-file-text-o:before { content: @fa-var-file-text-o; } .@{fa-css-prefix}-building-o:before { content: @fa-var-building-o; } .@{fa-css-prefix}-hospital-o:before { content: @fa-var-hospital-o; } .@{fa-css-prefix}-ambulance:before { content: @fa-var-ambulance; } .@{fa-css-prefix}-medkit:before { content: @fa-var-medkit; } .@{fa-css-prefix}-fighter-jet:before { content: @fa-var-fighter-jet; } .@{fa-css-prefix}-beer:before { content: @fa-var-beer; } .@{fa-css-prefix}-h-square:before { content: @fa-var-h-square; } .@{fa-css-prefix}-plus-square:before { content: @fa-var-plus-square; } .@{fa-css-prefix}-angle-double-left:before { content: @fa-var-angle-double-left; } .@{fa-css-prefix}-angle-double-right:before { content: @fa-var-angle-double-right; } .@{fa-css-prefix}-angle-double-up:before { content: @fa-var-angle-double-up; } .@{fa-css-prefix}-angle-double-down:before { content: @fa-var-angle-double-down; } .@{fa-css-prefix}-angle-left:before { content: @fa-var-angle-left; } .@{fa-css-prefix}-angle-right:before { content: @fa-var-angle-right; } .@{fa-css-prefix}-angle-up:before { content: @fa-var-angle-up; } .@{fa-css-prefix}-angle-down:before { content: @fa-var-angle-down; } .@{fa-css-prefix}-desktop:before { content: @fa-var-desktop; } .@{fa-css-prefix}-laptop:before { content: @fa-var-laptop; } .@{fa-css-prefix}-tablet:before { content: @fa-var-tablet; } .@{fa-css-prefix}-mobile-phone:before, .@{fa-css-prefix}-mobile:before { content: @fa-var-mobile; } .@{fa-css-prefix}-circle-o:before { content: @fa-var-circle-o; } .@{fa-css-prefix}-quote-left:before { content: @fa-var-quote-left; } .@{fa-css-prefix}-quote-right:before { content: @fa-var-quote-right; } .@{fa-css-prefix}-spinner:before { content: @fa-var-spinner; } .@{fa-css-prefix}-circle:before { content: @fa-var-circle; } .@{fa-css-prefix}-mail-reply:before, .@{fa-css-prefix}-reply:before { content: @fa-var-reply; } .@{fa-css-prefix}-github-alt:before { content: @fa-var-github-alt; } .@{fa-css-prefix}-folder-o:before { content: @fa-var-folder-o; } .@{fa-css-prefix}-folder-open-o:before { content: @fa-var-folder-open-o; } .@{fa-css-prefix}-smile-o:before { content: @fa-var-smile-o; } .@{fa-css-prefix}-frown-o:before { content: @fa-var-frown-o; } .@{fa-css-prefix}-meh-o:before { content: @fa-var-meh-o; } .@{fa-css-prefix}-gamepad:before { content: @fa-var-gamepad; } .@{fa-css-prefix}-keyboard-o:before { content: @fa-var-keyboard-o; } .@{fa-css-prefix}-flag-o:before { content: @fa-var-flag-o; } .@{fa-css-prefix}-flag-checkered:before { content: @fa-var-flag-checkered; } .@{fa-css-prefix}-terminal:before { content: @fa-var-terminal; } .@{fa-css-prefix}-code:before { content: @fa-var-code; } .@{fa-css-prefix}-mail-reply-all:before, .@{fa-css-prefix}-reply-all:before { content: @fa-var-reply-all; } .@{fa-css-prefix}-star-half-empty:before, .@{fa-css-prefix}-star-half-full:before, .@{fa-css-prefix}-star-half-o:before { content: @fa-var-star-half-o; } .@{fa-css-prefix}-location-arrow:before { content: @fa-var-location-arrow; } .@{fa-css-prefix}-crop:before { content: @fa-var-crop; } .@{fa-css-prefix}-code-fork:before { content: @fa-var-code-fork; } .@{fa-css-prefix}-unlink:before, .@{fa-css-prefix}-chain-broken:before { content: @fa-var-chain-broken; } .@{fa-css-prefix}-question:before { content: @fa-var-question; } .@{fa-css-prefix}-info:before { content: @fa-var-info; } .@{fa-css-prefix}-exclamation:before { content: @fa-var-exclamation; } .@{fa-css-prefix}-superscript:before { content: @fa-var-superscript; } .@{fa-css-prefix}-subscript:before { content: @fa-var-subscript; } .@{fa-css-prefix}-eraser:before { content: @fa-var-eraser; } .@{fa-css-prefix}-puzzle-piece:before { content: @fa-var-puzzle-piece; } .@{fa-css-prefix}-microphone:before { content: @fa-var-microphone; } .@{fa-css-prefix}-microphone-slash:before { content: @fa-var-microphone-slash; } .@{fa-css-prefix}-shield:before { content: @fa-var-shield; } .@{fa-css-prefix}-calendar-o:before { content: @fa-var-calendar-o; } .@{fa-css-prefix}-fire-extinguisher:before { content: @fa-var-fire-extinguisher; } .@{fa-css-prefix}-rocket:before { content: @fa-var-rocket; } .@{fa-css-prefix}-maxcdn:before { content: @fa-var-maxcdn; } .@{fa-css-prefix}-chevron-circle-left:before { content: @fa-var-chevron-circle-left; } .@{fa-css-prefix}-chevron-circle-right:before { content: @fa-var-chevron-circle-right; } .@{fa-css-prefix}-chevron-circle-up:before { content: @fa-var-chevron-circle-up; } .@{fa-css-prefix}-chevron-circle-down:before { content: @fa-var-chevron-circle-down; } .@{fa-css-prefix}-html5:before { content: @fa-var-html5; } .@{fa-css-prefix}-css3:before { content: @fa-var-css3; } .@{fa-css-prefix}-anchor:before { content: @fa-var-anchor; } .@{fa-css-prefix}-unlock-alt:before { content: @fa-var-unlock-alt; } .@{fa-css-prefix}-bullseye:before { content: @fa-var-bullseye; } .@{fa-css-prefix}-ellipsis-h:before { content: @fa-var-ellipsis-h; } .@{fa-css-prefix}-ellipsis-v:before { content: @fa-var-ellipsis-v; } .@{fa-css-prefix}-rss-square:before { content: @fa-var-rss-square; } .@{fa-css-prefix}-play-circle:before { content: @fa-var-play-circle; } .@{fa-css-prefix}-ticket:before { content: @fa-var-ticket; } .@{fa-css-prefix}-minus-square:before { content: @fa-var-minus-square; } .@{fa-css-prefix}-minus-square-o:before { content: @fa-var-minus-square-o; } .@{fa-css-prefix}-level-up:before { content: @fa-var-level-up; } .@{fa-css-prefix}-level-down:before { content: @fa-var-level-down; } .@{fa-css-prefix}-check-square:before { content: @fa-var-check-square; } .@{fa-css-prefix}-pencil-square:before { content: @fa-var-pencil-square; } .@{fa-css-prefix}-external-link-square:before { content: @fa-var-external-link-square; } .@{fa-css-prefix}-share-square:before { content: @fa-var-share-square; } .@{fa-css-prefix}-compass:before { content: @fa-var-compass; } .@{fa-css-prefix}-toggle-down:before, .@{fa-css-prefix}-caret-square-o-down:before { content: @fa-var-caret-square-o-down; } .@{fa-css-prefix}-toggle-up:before, .@{fa-css-prefix}-caret-square-o-up:before { content: @fa-var-caret-square-o-up; } .@{fa-css-prefix}-toggle-right:before, .@{fa-css-prefix}-caret-square-o-right:before { content: @fa-var-caret-square-o-right; } .@{fa-css-prefix}-euro:before, .@{fa-css-prefix}-eur:before { content: @fa-var-eur; } .@{fa-css-prefix}-gbp:before { content: @fa-var-gbp; } .@{fa-css-prefix}-dollar:before, .@{fa-css-prefix}-usd:before { content: @fa-var-usd; } .@{fa-css-prefix}-rupee:before, .@{fa-css-prefix}-inr:before { content: @fa-var-inr; } .@{fa-css-prefix}-cny:before, .@{fa-css-prefix}-rmb:before, .@{fa-css-prefix}-yen:before, .@{fa-css-prefix}-jpy:before { content: @fa-var-jpy; } .@{fa-css-prefix}-ruble:before, .@{fa-css-prefix}-rouble:before, .@{fa-css-prefix}-rub:before { content: @fa-var-rub; } .@{fa-css-prefix}-won:before, .@{fa-css-prefix}-krw:before { content: @fa-var-krw; } .@{fa-css-prefix}-bitcoin:before, .@{fa-css-prefix}-btc:before { content: @fa-var-btc; } .@{fa-css-prefix}-file:before { content: @fa-var-file; } .@{fa-css-prefix}-file-text:before { content: @fa-var-file-text; } .@{fa-css-prefix}-sort-alpha-asc:before { content: @fa-var-sort-alpha-asc; } .@{fa-css-prefix}-sort-alpha-desc:before { content: @fa-var-sort-alpha-desc; } .@{fa-css-prefix}-sort-amount-asc:before { content: @fa-var-sort-amount-asc; } .@{fa-css-prefix}-sort-amount-desc:before { content: @fa-var-sort-amount-desc; } .@{fa-css-prefix}-sort-numeric-asc:before { content: @fa-var-sort-numeric-asc; } .@{fa-css-prefix}-sort-numeric-desc:before { content: @fa-var-sort-numeric-desc; } .@{fa-css-prefix}-thumbs-up:before { content: @fa-var-thumbs-up; } .@{fa-css-prefix}-thumbs-down:before { content: @fa-var-thumbs-down; } .@{fa-css-prefix}-youtube-square:before { content: @fa-var-youtube-square; } .@{fa-css-prefix}-youtube:before { content: @fa-var-youtube; } .@{fa-css-prefix}-xing:before { content: @fa-var-xing; } .@{fa-css-prefix}-xing-square:before { content: @fa-var-xing-square; } .@{fa-css-prefix}-youtube-play:before { content: @fa-var-youtube-play; } .@{fa-css-prefix}-dropbox:before { content: @fa-var-dropbox; } .@{fa-css-prefix}-stack-overflow:before { content: @fa-var-stack-overflow; } .@{fa-css-prefix}-instagram:before { content: @fa-var-instagram; } .@{fa-css-prefix}-flickr:before { content: @fa-var-flickr; } .@{fa-css-prefix}-adn:before { content: @fa-var-adn; } .@{fa-css-prefix}-bitbucket:before { content: @fa-var-bitbucket; } .@{fa-css-prefix}-bitbucket-square:before { content: @fa-var-bitbucket-square; } .@{fa-css-prefix}-tumblr:before { content: @fa-var-tumblr; } .@{fa-css-prefix}-tumblr-square:before { content: @fa-var-tumblr-square; } .@{fa-css-prefix}-long-arrow-down:before { content: @fa-var-long-arrow-down; } .@{fa-css-prefix}-long-arrow-up:before { content: @fa-var-long-arrow-up; } .@{fa-css-prefix}-long-arrow-left:before { content: @fa-var-long-arrow-left; } .@{fa-css-prefix}-long-arrow-right:before { content: @fa-var-long-arrow-right; } .@{fa-css-prefix}-apple:before { content: @fa-var-apple; } .@{fa-css-prefix}-windows:before { content: @fa-var-windows; } .@{fa-css-prefix}-android:before { content: @fa-var-android; } .@{fa-css-prefix}-linux:before { content: @fa-var-linux; } .@{fa-css-prefix}-dribbble:before { content: @fa-var-dribbble; } .@{fa-css-prefix}-skype:before { content: @fa-var-skype; } .@{fa-css-prefix}-foursquare:before { content: @fa-var-foursquare; } .@{fa-css-prefix}-trello:before { content: @fa-var-trello; } .@{fa-css-prefix}-female:before { content: @fa-var-female; } .@{fa-css-prefix}-male:before { content: @fa-var-male; } .@{fa-css-prefix}-gittip:before, .@{fa-css-prefix}-gratipay:before { content: @fa-var-gratipay; } .@{fa-css-prefix}-sun-o:before { content: @fa-var-sun-o; } .@{fa-css-prefix}-moon-o:before { content: @fa-var-moon-o; } .@{fa-css-prefix}-archive:before { content: @fa-var-archive; } .@{fa-css-prefix}-bug:before { content: @fa-var-bug; } .@{fa-css-prefix}-vk:before { content: @fa-var-vk; } .@{fa-css-prefix}-weibo:before { content: @fa-var-weibo; } .@{fa-css-prefix}-renren:before { content: @fa-var-renren; } .@{fa-css-prefix}-pagelines:before { content: @fa-var-pagelines; } .@{fa-css-prefix}-stack-exchange:before { content: @fa-var-stack-exchange; } .@{fa-css-prefix}-arrow-circle-o-right:before { content: @fa-var-arrow-circle-o-right; } .@{fa-css-prefix}-arrow-circle-o-left:before { content: @fa-var-arrow-circle-o-left; } .@{fa-css-prefix}-toggle-left:before, .@{fa-css-prefix}-caret-square-o-left:before { content: @fa-var-caret-square-o-left; } .@{fa-css-prefix}-dot-circle-o:before { content: @fa-var-dot-circle-o; } .@{fa-css-prefix}-wheelchair:before { content: @fa-var-wheelchair; } .@{fa-css-prefix}-vimeo-square:before { content: @fa-var-vimeo-square; } .@{fa-css-prefix}-turkish-lira:before, .@{fa-css-prefix}-try:before { content: @fa-var-try; } .@{fa-css-prefix}-plus-square-o:before { content: @fa-var-plus-square-o; } .@{fa-css-prefix}-space-shuttle:before { content: @fa-var-space-shuttle; } .@{fa-css-prefix}-slack:before { content: @fa-var-slack; } .@{fa-css-prefix}-envelope-square:before { content: @fa-var-envelope-square; } .@{fa-css-prefix}-wordpress:before { content: @fa-var-wordpress; } .@{fa-css-prefix}-openid:before { content: @fa-var-openid; } .@{fa-css-prefix}-institution:before, .@{fa-css-prefix}-bank:before, .@{fa-css-prefix}-university:before { content: @fa-var-university; } .@{fa-css-prefix}-mortar-board:before, .@{fa-css-prefix}-graduation-cap:before { content: @fa-var-graduation-cap; } .@{fa-css-prefix}-yahoo:before { content: @fa-var-yahoo; } .@{fa-css-prefix}-google:before { content: @fa-var-google; } .@{fa-css-prefix}-reddit:before { content: @fa-var-reddit; } .@{fa-css-prefix}-reddit-square:before { content: @fa-var-reddit-square; } .@{fa-css-prefix}-stumbleupon-circle:before { content: @fa-var-stumbleupon-circle; } .@{fa-css-prefix}-stumbleupon:before { content: @fa-var-stumbleupon; } .@{fa-css-prefix}-delicious:before { content: @fa-var-delicious; } .@{fa-css-prefix}-digg:before { content: @fa-var-digg; } .@{fa-css-prefix}-pied-piper-pp:before { content: @fa-var-pied-piper-pp; } .@{fa-css-prefix}-pied-piper-alt:before { content: @fa-var-pied-piper-alt; } .@{fa-css-prefix}-drupal:before { content: @fa-var-drupal; } .@{fa-css-prefix}-joomla:before { content: @fa-var-joomla; } .@{fa-css-prefix}-language:before { content: @fa-var-language; } .@{fa-css-prefix}-fax:before { content: @fa-var-fax; } .@{fa-css-prefix}-building:before { content: @fa-var-building; } .@{fa-css-prefix}-child:before { content: @fa-var-child; } .@{fa-css-prefix}-paw:before { content: @fa-var-paw; } .@{fa-css-prefix}-spoon:before { content: @fa-var-spoon; } .@{fa-css-prefix}-cube:before { content: @fa-var-cube; } .@{fa-css-prefix}-cubes:before { content: @fa-var-cubes; } .@{fa-css-prefix}-behance:before { content: @fa-var-behance; } .@{fa-css-prefix}-behance-square:before { content: @fa-var-behance-square; } .@{fa-css-prefix}-steam:before { content: @fa-var-steam; } .@{fa-css-prefix}-steam-square:before { content: @fa-var-steam-square; } .@{fa-css-prefix}-recycle:before { content: @fa-var-recycle; } .@{fa-css-prefix}-automobile:before, .@{fa-css-prefix}-car:before { content: @fa-var-car; } .@{fa-css-prefix}-cab:before, .@{fa-css-prefix}-taxi:before { content: @fa-var-taxi; } .@{fa-css-prefix}-tree:before { content: @fa-var-tree; } .@{fa-css-prefix}-spotify:before { content: @fa-var-spotify; } .@{fa-css-prefix}-deviantart:before { content: @fa-var-deviantart; } .@{fa-css-prefix}-soundcloud:before { content: @fa-var-soundcloud; } .@{fa-css-prefix}-database:before { content: @fa-var-database; } .@{fa-css-prefix}-file-pdf-o:before { content: @fa-var-file-pdf-o; } .@{fa-css-prefix}-file-word-o:before { content: @fa-var-file-word-o; } .@{fa-css-prefix}-file-excel-o:before { content: @fa-var-file-excel-o; } .@{fa-css-prefix}-file-powerpoint-o:before { content: @fa-var-file-powerpoint-o; } .@{fa-css-prefix}-file-photo-o:before, .@{fa-css-prefix}-file-picture-o:before, .@{fa-css-prefix}-file-image-o:before { content: @fa-var-file-image-o; } .@{fa-css-prefix}-file-zip-o:before, .@{fa-css-prefix}-file-archive-o:before { content: @fa-var-file-archive-o; } .@{fa-css-prefix}-file-sound-o:before, .@{fa-css-prefix}-file-audio-o:before { content: @fa-var-file-audio-o; } .@{fa-css-prefix}-file-movie-o:before, .@{fa-css-prefix}-file-video-o:before { content: @fa-var-file-video-o; } .@{fa-css-prefix}-file-code-o:before { content: @fa-var-file-code-o; } .@{fa-css-prefix}-vine:before { content: @fa-var-vine; } .@{fa-css-prefix}-codepen:before { content: @fa-var-codepen; } .@{fa-css-prefix}-jsfiddle:before { content: @fa-var-jsfiddle; } .@{fa-css-prefix}-life-bouy:before, .@{fa-css-prefix}-life-buoy:before, .@{fa-css-prefix}-life-saver:before, .@{fa-css-prefix}-support:before, .@{fa-css-prefix}-life-ring:before { content: @fa-var-life-ring; } .@{fa-css-prefix}-circle-o-notch:before { content: @fa-var-circle-o-notch; } .@{fa-css-prefix}-ra:before, .@{fa-css-prefix}-resistance:before, .@{fa-css-prefix}-rebel:before { content: @fa-var-rebel; } .@{fa-css-prefix}-ge:before, .@{fa-css-prefix}-empire:before { content: @fa-var-empire; } .@{fa-css-prefix}-git-square:before { content: @fa-var-git-square; } .@{fa-css-prefix}-git:before { content: @fa-var-git; } .@{fa-css-prefix}-y-combinator-square:before, .@{fa-css-prefix}-yc-square:before, .@{fa-css-prefix}-hacker-news:before { content: @fa-var-hacker-news; } .@{fa-css-prefix}-tencent-weibo:before { content: @fa-var-tencent-weibo; } .@{fa-css-prefix}-qq:before { content: @fa-var-qq; } .@{fa-css-prefix}-wechat:before, .@{fa-css-prefix}-weixin:before { content: @fa-var-weixin; } .@{fa-css-prefix}-send:before, .@{fa-css-prefix}-paper-plane:before { content: @fa-var-paper-plane; } .@{fa-css-prefix}-send-o:before, .@{fa-css-prefix}-paper-plane-o:before { content: @fa-var-paper-plane-o; } .@{fa-css-prefix}-history:before { content: @fa-var-history; } .@{fa-css-prefix}-circle-thin:before { content: @fa-var-circle-thin; } .@{fa-css-prefix}-header:before { content: @fa-var-header; } .@{fa-css-prefix}-paragraph:before { content: @fa-var-paragraph; } .@{fa-css-prefix}-sliders:before { content: @fa-var-sliders; } .@{fa-css-prefix}-share-alt:before { content: @fa-var-share-alt; } .@{fa-css-prefix}-share-alt-square:before { content: @fa-var-share-alt-square; } .@{fa-css-prefix}-bomb:before { content: @fa-var-bomb; } .@{fa-css-prefix}-soccer-ball-o:before, .@{fa-css-prefix}-futbol-o:before { content: @fa-var-futbol-o; } .@{fa-css-prefix}-tty:before { content: @fa-var-tty; } .@{fa-css-prefix}-binoculars:before { content: @fa-var-binoculars; } .@{fa-css-prefix}-plug:before { content: @fa-var-plug; } .@{fa-css-prefix}-slideshare:before { content: @fa-var-slideshare; } .@{fa-css-prefix}-twitch:before { content: @fa-var-twitch; } .@{fa-css-prefix}-yelp:before { content: @fa-var-yelp; } .@{fa-css-prefix}-newspaper-o:before { content: @fa-var-newspaper-o; } .@{fa-css-prefix}-wifi:before { content: @fa-var-wifi; } .@{fa-css-prefix}-calculator:before { content: @fa-var-calculator; } .@{fa-css-prefix}-paypal:before { content: @fa-var-paypal; } .@{fa-css-prefix}-google-wallet:before { content: @fa-var-google-wallet; } .@{fa-css-prefix}-cc-visa:before { content: @fa-var-cc-visa; } .@{fa-css-prefix}-cc-mastercard:before { content: @fa-var-cc-mastercard; } .@{fa-css-prefix}-cc-discover:before { content: @fa-var-cc-discover; } .@{fa-css-prefix}-cc-amex:before { content: @fa-var-cc-amex; } .@{fa-css-prefix}-cc-paypal:before { content: @fa-var-cc-paypal; } .@{fa-css-prefix}-cc-stripe:before { content: @fa-var-cc-stripe; } .@{fa-css-prefix}-bell-slash:before { content: @fa-var-bell-slash; } .@{fa-css-prefix}-bell-slash-o:before { content: @fa-var-bell-slash-o; } .@{fa-css-prefix}-trash:before { content: @fa-var-trash; } .@{fa-css-prefix}-copyright:before { content: @fa-var-copyright; } .@{fa-css-prefix}-at:before { content: @fa-var-at; } .@{fa-css-prefix}-eyedropper:before { content: @fa-var-eyedropper; } .@{fa-css-prefix}-paint-brush:before { content: @fa-var-paint-brush; } .@{fa-css-prefix}-birthday-cake:before { content: @fa-var-birthday-cake; } .@{fa-css-prefix}-area-chart:before { content: @fa-var-area-chart; } .@{fa-css-prefix}-pie-chart:before { content: @fa-var-pie-chart; } .@{fa-css-prefix}-line-chart:before { content: @fa-var-line-chart; } .@{fa-css-prefix}-lastfm:before { content: @fa-var-lastfm; } .@{fa-css-prefix}-lastfm-square:before { content: @fa-var-lastfm-square; } .@{fa-css-prefix}-toggle-off:before { content: @fa-var-toggle-off; } .@{fa-css-prefix}-toggle-on:before { content: @fa-var-toggle-on; } .@{fa-css-prefix}-bicycle:before { content: @fa-var-bicycle; } .@{fa-css-prefix}-bus:before { content: @fa-var-bus; } .@{fa-css-prefix}-ioxhost:before { content: @fa-var-ioxhost; } .@{fa-css-prefix}-angellist:before { content: @fa-var-angellist; } .@{fa-css-prefix}-cc:before { content: @fa-var-cc; } .@{fa-css-prefix}-shekel:before, .@{fa-css-prefix}-sheqel:before, .@{fa-css-prefix}-ils:before { content: @fa-var-ils; } .@{fa-css-prefix}-meanpath:before { content: @fa-var-meanpath; } .@{fa-css-prefix}-buysellads:before { content: @fa-var-buysellads; } .@{fa-css-prefix}-connectdevelop:before { content: @fa-var-connectdevelop; } .@{fa-css-prefix}-dashcube:before { content: @fa-var-dashcube; } .@{fa-css-prefix}-forumbee:before { content: @fa-var-forumbee; } .@{fa-css-prefix}-leanpub:before { content: @fa-var-leanpub; } .@{fa-css-prefix}-sellsy:before { content: @fa-var-sellsy; } .@{fa-css-prefix}-shirtsinbulk:before { content: @fa-var-shirtsinbulk; } .@{fa-css-prefix}-simplybuilt:before { content: @fa-var-simplybuilt; } .@{fa-css-prefix}-skyatlas:before { content: @fa-var-skyatlas; } .@{fa-css-prefix}-cart-plus:before { content: @fa-var-cart-plus; } .@{fa-css-prefix}-cart-arrow-down:before { content: @fa-var-cart-arrow-down; } .@{fa-css-prefix}-diamond:before { content: @fa-var-diamond; } .@{fa-css-prefix}-ship:before { content: @fa-var-ship; } .@{fa-css-prefix}-user-secret:before { content: @fa-var-user-secret; } .@{fa-css-prefix}-motorcycle:before { content: @fa-var-motorcycle; } .@{fa-css-prefix}-street-view:before { content: @fa-var-street-view; } .@{fa-css-prefix}-heartbeat:before { content: @fa-var-heartbeat; } .@{fa-css-prefix}-venus:before { content: @fa-var-venus; } .@{fa-css-prefix}-mars:before { content: @fa-var-mars; } .@{fa-css-prefix}-mercury:before { content: @fa-var-mercury; } .@{fa-css-prefix}-intersex:before, .@{fa-css-prefix}-transgender:before { content: @fa-var-transgender; } .@{fa-css-prefix}-transgender-alt:before { content: @fa-var-transgender-alt; } .@{fa-css-prefix}-venus-double:before { content: @fa-var-venus-double; } .@{fa-css-prefix}-mars-double:before { content: @fa-var-mars-double; } .@{fa-css-prefix}-venus-mars:before { content: @fa-var-venus-mars; } .@{fa-css-prefix}-mars-stroke:before { content: @fa-var-mars-stroke; } .@{fa-css-prefix}-mars-stroke-v:before { content: @fa-var-mars-stroke-v; } .@{fa-css-prefix}-mars-stroke-h:before { content: @fa-var-mars-stroke-h; } .@{fa-css-prefix}-neuter:before { content: @fa-var-neuter; } .@{fa-css-prefix}-genderless:before { content: @fa-var-genderless; } .@{fa-css-prefix}-facebook-official:before { content: @fa-var-facebook-official; } .@{fa-css-prefix}-pinterest-p:before { content: @fa-var-pinterest-p; } .@{fa-css-prefix}-whatsapp:before { content: @fa-var-whatsapp; } .@{fa-css-prefix}-server:before { content: @fa-var-server; } .@{fa-css-prefix}-user-plus:before { content: @fa-var-user-plus; } .@{fa-css-prefix}-user-times:before { content: @fa-var-user-times; } .@{fa-css-prefix}-hotel:before, .@{fa-css-prefix}-bed:before { content: @fa-var-bed; } .@{fa-css-prefix}-viacoin:before { content: @fa-var-viacoin; } .@{fa-css-prefix}-train:before { content: @fa-var-train; } .@{fa-css-prefix}-subway:before { content: @fa-var-subway; } .@{fa-css-prefix}-medium:before { content: @fa-var-medium; } .@{fa-css-prefix}-yc:before, .@{fa-css-prefix}-y-combinator:before { content: @fa-var-y-combinator; } .@{fa-css-prefix}-optin-monster:before { content: @fa-var-optin-monster; } .@{fa-css-prefix}-opencart:before { content: @fa-var-opencart; } .@{fa-css-prefix}-expeditedssl:before { content: @fa-var-expeditedssl; } .@{fa-css-prefix}-battery-4:before, .@{fa-css-prefix}-battery:before, .@{fa-css-prefix}-battery-full:before { content: @fa-var-battery-full; } .@{fa-css-prefix}-battery-3:before, .@{fa-css-prefix}-battery-three-quarters:before { content: @fa-var-battery-three-quarters; } .@{fa-css-prefix}-battery-2:before, .@{fa-css-prefix}-battery-half:before { content: @fa-var-battery-half; } .@{fa-css-prefix}-battery-1:before, .@{fa-css-prefix}-battery-quarter:before { content: @fa-var-battery-quarter; } .@{fa-css-prefix}-battery-0:before, .@{fa-css-prefix}-battery-empty:before { content: @fa-var-battery-empty; } .@{fa-css-prefix}-mouse-pointer:before { content: @fa-var-mouse-pointer; } .@{fa-css-prefix}-i-cursor:before { content: @fa-var-i-cursor; } .@{fa-css-prefix}-object-group:before { content: @fa-var-object-group; } .@{fa-css-prefix}-object-ungroup:before { content: @fa-var-object-ungroup; } .@{fa-css-prefix}-sticky-note:before { content: @fa-var-sticky-note; } .@{fa-css-prefix}-sticky-note-o:before { content: @fa-var-sticky-note-o; } .@{fa-css-prefix}-cc-jcb:before { content: @fa-var-cc-jcb; } .@{fa-css-prefix}-cc-diners-club:before { content: @fa-var-cc-diners-club; } .@{fa-css-prefix}-clone:before { content: @fa-var-clone; } .@{fa-css-prefix}-balance-scale:before { content: @fa-var-balance-scale; } .@{fa-css-prefix}-hourglass-o:before { content: @fa-var-hourglass-o; } .@{fa-css-prefix}-hourglass-1:before, .@{fa-css-prefix}-hourglass-start:before { content: @fa-var-hourglass-start; } .@{fa-css-prefix}-hourglass-2:before, .@{fa-css-prefix}-hourglass-half:before { content: @fa-var-hourglass-half; } .@{fa-css-prefix}-hourglass-3:before, .@{fa-css-prefix}-hourglass-end:before { content: @fa-var-hourglass-end; } .@{fa-css-prefix}-hourglass:before { content: @fa-var-hourglass; } .@{fa-css-prefix}-hand-grab-o:before, .@{fa-css-prefix}-hand-rock-o:before { content: @fa-var-hand-rock-o; } .@{fa-css-prefix}-hand-stop-o:before, .@{fa-css-prefix}-hand-paper-o:before { content: @fa-var-hand-paper-o; } .@{fa-css-prefix}-hand-scissors-o:before { content: @fa-var-hand-scissors-o; } .@{fa-css-prefix}-hand-lizard-o:before { content: @fa-var-hand-lizard-o; } .@{fa-css-prefix}-hand-spock-o:before { content: @fa-var-hand-spock-o; } .@{fa-css-prefix}-hand-pointer-o:before { content: @fa-var-hand-pointer-o; } .@{fa-css-prefix}-hand-peace-o:before { content: @fa-var-hand-peace-o; } .@{fa-css-prefix}-trademark:before { content: @fa-var-trademark; } .@{fa-css-prefix}-registered:before { content: @fa-var-registered; } .@{fa-css-prefix}-creative-commons:before { content: @fa-var-creative-commons; } .@{fa-css-prefix}-gg:before { content: @fa-var-gg; } .@{fa-css-prefix}-gg-circle:before { content: @fa-var-gg-circle; } .@{fa-css-prefix}-tripadvisor:before { content: @fa-var-tripadvisor; } .@{fa-css-prefix}-odnoklassniki:before { content: @fa-var-odnoklassniki; } .@{fa-css-prefix}-odnoklassniki-square:before { content: @fa-var-odnoklassniki-square; } .@{fa-css-prefix}-get-pocket:before { content: @fa-var-get-pocket; } .@{fa-css-prefix}-wikipedia-w:before { content: @fa-var-wikipedia-w; } .@{fa-css-prefix}-safari:before { content: @fa-var-safari; } .@{fa-css-prefix}-chrome:before { content: @fa-var-chrome; } .@{fa-css-prefix}-firefox:before { content: @fa-var-firefox; } .@{fa-css-prefix}-opera:before { content: @fa-var-opera; } .@{fa-css-prefix}-internet-explorer:before { content: @fa-var-internet-explorer; } .@{fa-css-prefix}-tv:before, .@{fa-css-prefix}-television:before { content: @fa-var-television; } .@{fa-css-prefix}-contao:before { content: @fa-var-contao; } .@{fa-css-prefix}-500px:before { content: @fa-var-500px; } .@{fa-css-prefix}-amazon:before { content: @fa-var-amazon; } .@{fa-css-prefix}-calendar-plus-o:before { content: @fa-var-calendar-plus-o; } .@{fa-css-prefix}-calendar-minus-o:before { content: @fa-var-calendar-minus-o; } .@{fa-css-prefix}-calendar-times-o:before { content: @fa-var-calendar-times-o; } .@{fa-css-prefix}-calendar-check-o:before { content: @fa-var-calendar-check-o; } .@{fa-css-prefix}-industry:before { content: @fa-var-industry; } .@{fa-css-prefix}-map-pin:before { content: @fa-var-map-pin; } .@{fa-css-prefix}-map-signs:before { content: @fa-var-map-signs; } .@{fa-css-prefix}-map-o:before { content: @fa-var-map-o; } .@{fa-css-prefix}-map:before { content: @fa-var-map; } .@{fa-css-prefix}-commenting:before { content: @fa-var-commenting; } .@{fa-css-prefix}-commenting-o:before { content: @fa-var-commenting-o; } .@{fa-css-prefix}-houzz:before { content: @fa-var-houzz; } .@{fa-css-prefix}-vimeo:before { content: @fa-var-vimeo; } .@{fa-css-prefix}-black-tie:before { content: @fa-var-black-tie; } .@{fa-css-prefix}-fonticons:before { content: @fa-var-fonticons; } .@{fa-css-prefix}-reddit-alien:before { content: @fa-var-reddit-alien; } .@{fa-css-prefix}-edge:before { content: @fa-var-edge; } .@{fa-css-prefix}-credit-card-alt:before { content: @fa-var-credit-card-alt; } .@{fa-css-prefix}-codiepie:before { content: @fa-var-codiepie; } .@{fa-css-prefix}-modx:before { content: @fa-var-modx; } .@{fa-css-prefix}-fort-awesome:before { content: @fa-var-fort-awesome; } .@{fa-css-prefix}-usb:before { content: @fa-var-usb; } .@{fa-css-prefix}-product-hunt:before { content: @fa-var-product-hunt; } .@{fa-css-prefix}-mixcloud:before { content: @fa-var-mixcloud; } .@{fa-css-prefix}-scribd:before { content: @fa-var-scribd; } .@{fa-css-prefix}-pause-circle:before { content: @fa-var-pause-circle; } .@{fa-css-prefix}-pause-circle-o:before { content: @fa-var-pause-circle-o; } .@{fa-css-prefix}-stop-circle:before { content: @fa-var-stop-circle; } .@{fa-css-prefix}-stop-circle-o:before { content: @fa-var-stop-circle-o; } .@{fa-css-prefix}-shopping-bag:before { content: @fa-var-shopping-bag; } .@{fa-css-prefix}-shopping-basket:before { content: @fa-var-shopping-basket; } .@{fa-css-prefix}-hashtag:before { content: @fa-var-hashtag; } .@{fa-css-prefix}-bluetooth:before { content: @fa-var-bluetooth; } .@{fa-css-prefix}-bluetooth-b:before { content: @fa-var-bluetooth-b; } .@{fa-css-prefix}-percent:before { content: @fa-var-percent; } .@{fa-css-prefix}-gitlab:before { content: @fa-var-gitlab; } .@{fa-css-prefix}-wpbeginner:before { content: @fa-var-wpbeginner; } .@{fa-css-prefix}-wpforms:before { content: @fa-var-wpforms; } .@{fa-css-prefix}-envira:before { content: @fa-var-envira; } .@{fa-css-prefix}-universal-access:before { content: @fa-var-universal-access; } .@{fa-css-prefix}-wheelchair-alt:before { content: @fa-var-wheelchair-alt; } .@{fa-css-prefix}-question-circle-o:before { content: @fa-var-question-circle-o; } .@{fa-css-prefix}-blind:before { content: @fa-var-blind; } .@{fa-css-prefix}-audio-description:before { content: @fa-var-audio-description; } .@{fa-css-prefix}-volume-control-phone:before { content: @fa-var-volume-control-phone; } .@{fa-css-prefix}-braille:before { content: @fa-var-braille; } .@{fa-css-prefix}-assistive-listening-systems:before { content: @fa-var-assistive-listening-systems; } .@{fa-css-prefix}-asl-interpreting:before, .@{fa-css-prefix}-american-sign-language-interpreting:before { content: @fa-var-american-sign-language-interpreting; } .@{fa-css-prefix}-deafness:before, .@{fa-css-prefix}-hard-of-hearing:before, .@{fa-css-prefix}-deaf:before { content: @fa-var-deaf; } .@{fa-css-prefix}-glide:before { content: @fa-var-glide; } .@{fa-css-prefix}-glide-g:before { content: @fa-var-glide-g; } .@{fa-css-prefix}-signing:before, .@{fa-css-prefix}-sign-language:before { content: @fa-var-sign-language; } .@{fa-css-prefix}-low-vision:before { content: @fa-var-low-vision; } .@{fa-css-prefix}-viadeo:before { content: @fa-var-viadeo; } .@{fa-css-prefix}-viadeo-square:before { content: @fa-var-viadeo-square; } .@{fa-css-prefix}-snapchat:before { content: @fa-var-snapchat; } .@{fa-css-prefix}-snapchat-ghost:before { content: @fa-var-snapchat-ghost; } .@{fa-css-prefix}-snapchat-square:before { content: @fa-var-snapchat-square; } .@{fa-css-prefix}-pied-piper:before { content: @fa-var-pied-piper; } .@{fa-css-prefix}-first-order:before { content: @fa-var-first-order; } .@{fa-css-prefix}-yoast:before { content: @fa-var-yoast; } .@{fa-css-prefix}-themeisle:before { content: @fa-var-themeisle; } .@{fa-css-prefix}-google-plus-circle:before, .@{fa-css-prefix}-google-plus-official:before { content: @fa-var-google-plus-official; } .@{fa-css-prefix}-fa:before, .@{fa-css-prefix}-font-awesome:before { content: @fa-var-font-awesome; } .@{fa-css-prefix}-handshake-o:before { content: @fa-var-handshake-o; } .@{fa-css-prefix}-envelope-open:before { content: @fa-var-envelope-open; } .@{fa-css-prefix}-envelope-open-o:before { content: @fa-var-envelope-open-o; } .@{fa-css-prefix}-linode:before { content: @fa-var-linode; } .@{fa-css-prefix}-address-book:before { content: @fa-var-address-book; } .@{fa-css-prefix}-address-book-o:before { content: @fa-var-address-book-o; } .@{fa-css-prefix}-vcard:before, .@{fa-css-prefix}-address-card:before { content: @fa-var-address-card; } .@{fa-css-prefix}-vcard-o:before, .@{fa-css-prefix}-address-card-o:before { content: @fa-var-address-card-o; } .@{fa-css-prefix}-user-circle:before { content: @fa-var-user-circle; } .@{fa-css-prefix}-user-circle-o:before { content: @fa-var-user-circle-o; } .@{fa-css-prefix}-user-o:before { content: @fa-var-user-o; } .@{fa-css-prefix}-id-badge:before { content: @fa-var-id-badge; } .@{fa-css-prefix}-drivers-license:before, .@{fa-css-prefix}-id-card:before { content: @fa-var-id-card; } .@{fa-css-prefix}-drivers-license-o:before, .@{fa-css-prefix}-id-card-o:before { content: @fa-var-id-card-o; } .@{fa-css-prefix}-quora:before { content: @fa-var-quora; } .@{fa-css-prefix}-free-code-camp:before { content: @fa-var-free-code-camp; } .@{fa-css-prefix}-telegram:before { content: @fa-var-telegram; } .@{fa-css-prefix}-thermometer-4:before, .@{fa-css-prefix}-thermometer:before, .@{fa-css-prefix}-thermometer-full:before { content: @fa-var-thermometer-full; } .@{fa-css-prefix}-thermometer-3:before, .@{fa-css-prefix}-thermometer-three-quarters:before { content: @fa-var-thermometer-three-quarters; } .@{fa-css-prefix}-thermometer-2:before, .@{fa-css-prefix}-thermometer-half:before { content: @fa-var-thermometer-half; } .@{fa-css-prefix}-thermometer-1:before, .@{fa-css-prefix}-thermometer-quarter:before { content: @fa-var-thermometer-quarter; } .@{fa-css-prefix}-thermometer-0:before, .@{fa-css-prefix}-thermometer-empty:before { content: @fa-var-thermometer-empty; } .@{fa-css-prefix}-shower:before { content: @fa-var-shower; } .@{fa-css-prefix}-bathtub:before, .@{fa-css-prefix}-s15:before, .@{fa-css-prefix}-bath:before { content: @fa-var-bath; } .@{fa-css-prefix}-podcast:before { content: @fa-var-podcast; } .@{fa-css-prefix}-window-maximize:before { content: @fa-var-window-maximize; } .@{fa-css-prefix}-window-minimize:before { content: @fa-var-window-minimize; } .@{fa-css-prefix}-window-restore:before { content: @fa-var-window-restore; } .@{fa-css-prefix}-times-rectangle:before, .@{fa-css-prefix}-window-close:before { content: @fa-var-window-close; } .@{fa-css-prefix}-times-rectangle-o:before, .@{fa-css-prefix}-window-close-o:before { content: @fa-var-window-close-o; } .@{fa-css-prefix}-bandcamp:before { content: @fa-var-bandcamp; } .@{fa-css-prefix}-grav:before { content: @fa-var-grav; } .@{fa-css-prefix}-etsy:before { content: @fa-var-etsy; } .@{fa-css-prefix}-imdb:before { content: @fa-var-imdb; } .@{fa-css-prefix}-ravelry:before { content: @fa-var-ravelry; } .@{fa-css-prefix}-eercast:before { content: @fa-var-eercast; } .@{fa-css-prefix}-microchip:before { content: @fa-var-microchip; } .@{fa-css-prefix}-snowflake-o:before { content: @fa-var-snowflake-o; } .@{fa-css-prefix}-superpowers:before { content: @fa-var-superpowers; } .@{fa-css-prefix}-wpexplorer:before { content: @fa-var-wpexplorer; } .@{fa-css-prefix}-meetup:before { content: @fa-var-meetup; } ================================================ FILE: static/font-awesome-4.7.0/less/larger.less ================================================ // Icon Sizes // ------------------------- /* makes the font 33% larger relative to the icon container */ .@{fa-css-prefix}-lg { font-size: (4em / 3); line-height: (3em / 4); vertical-align: -15%; } .@{fa-css-prefix}-2x { font-size: 2em; } .@{fa-css-prefix}-3x { font-size: 3em; } .@{fa-css-prefix}-4x { font-size: 4em; } .@{fa-css-prefix}-5x { font-size: 5em; } ================================================ FILE: static/font-awesome-4.7.0/less/list.less ================================================ // List Icons // ------------------------- .@{fa-css-prefix}-ul { padding-left: 0; margin-left: @fa-li-width; list-style-type: none; > li { position: relative; } } .@{fa-css-prefix}-li { position: absolute; left: -@fa-li-width; width: @fa-li-width; top: (2em / 14); text-align: center; &.@{fa-css-prefix}-lg { left: (-@fa-li-width + (4em / 14)); } } ================================================ FILE: static/font-awesome-4.7.0/less/mixins.less ================================================ // Mixins // -------------------------- .fa-icon() { display: inline-block; font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration font-size: inherit; // can't have font-size inherit on line above, so need to override text-rendering: auto; // optimizelegibility throws things off #1094 -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .fa-icon-rotate(@degrees, @rotation) { -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation})"; -webkit-transform: rotate(@degrees); -ms-transform: rotate(@degrees); transform: rotate(@degrees); } .fa-icon-flip(@horiz, @vert, @rotation) { -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation}, mirror=1)"; -webkit-transform: scale(@horiz, @vert); -ms-transform: scale(@horiz, @vert); transform: scale(@horiz, @vert); } // Only display content to screen readers. A la Bootstrap 4. // // See: http://a11yproject.com/posts/how-to-hide-content/ .sr-only() { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); border: 0; } // Use in conjunction with .sr-only to only display content when it's focused. // // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 // // Credit: HTML5 Boilerplate .sr-only-focusable() { &:active, &:focus { position: static; width: auto; height: auto; margin: 0; overflow: visible; clip: auto; } } ================================================ FILE: static/font-awesome-4.7.0/less/path.less ================================================ /* FONT PATH * -------------------------- */ @font-face { font-family: 'FontAwesome'; src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}'); src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype'), url('@{fa-font-path}/fontawesome-webfont.woff2?v=@{fa-version}') format('woff2'), url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'), url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'), url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg'); // src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts font-weight: normal; font-style: normal; } ================================================ FILE: static/font-awesome-4.7.0/less/rotated-flipped.less ================================================ // Rotated & Flipped Icons // ------------------------- .@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); } .@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); } .@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); } .@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); } .@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); } // Hook for IE8-9 // ------------------------- :root .@{fa-css-prefix}-rotate-90, :root .@{fa-css-prefix}-rotate-180, :root .@{fa-css-prefix}-rotate-270, :root .@{fa-css-prefix}-flip-horizontal, :root .@{fa-css-prefix}-flip-vertical { filter: none; } ================================================ FILE: static/font-awesome-4.7.0/less/screen-reader.less ================================================ // Screen Readers // ------------------------- .sr-only { .sr-only(); } .sr-only-focusable { .sr-only-focusable(); } ================================================ FILE: static/font-awesome-4.7.0/less/stacked.less ================================================ // Stacked Icons // ------------------------- .@{fa-css-prefix}-stack { position: relative; display: inline-block; width: 2em; height: 2em; line-height: 2em; vertical-align: middle; } .@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x { position: absolute; left: 0; width: 100%; text-align: center; } .@{fa-css-prefix}-stack-1x { line-height: inherit; } .@{fa-css-prefix}-stack-2x { font-size: 2em; } .@{fa-css-prefix}-inverse { color: @fa-inverse; } ================================================ FILE: static/font-awesome-4.7.0/less/variables.less ================================================ // Variables // -------------------------- @fa-font-path: "../fonts"; @fa-font-size-base: 14px; @fa-line-height-base: 1; //@fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.7.0/fonts"; // for referencing Bootstrap CDN font files directly @fa-css-prefix: fa; @fa-version: "4.7.0"; @fa-border-color: #eee; @fa-inverse: #fff; @fa-li-width: (30em / 14); @fa-var-500px: "\f26e"; @fa-var-address-book: "\f2b9"; @fa-var-address-book-o: "\f2ba"; @fa-var-address-card: "\f2bb"; @fa-var-address-card-o: "\f2bc"; @fa-var-adjust: "\f042"; @fa-var-adn: "\f170"; @fa-var-align-center: "\f037"; @fa-var-align-justify: "\f039"; @fa-var-align-left: "\f036"; @fa-var-align-right: "\f038"; @fa-var-amazon: "\f270"; @fa-var-ambulance: "\f0f9"; @fa-var-american-sign-language-interpreting: "\f2a3"; @fa-var-anchor: "\f13d"; @fa-var-android: "\f17b"; @fa-var-angellist: "\f209"; @fa-var-angle-double-down: "\f103"; @fa-var-angle-double-left: "\f100"; @fa-var-angle-double-right: "\f101"; @fa-var-angle-double-up: "\f102"; @fa-var-angle-down: "\f107"; @fa-var-angle-left: "\f104"; @fa-var-angle-right: "\f105"; @fa-var-angle-up: "\f106"; @fa-var-apple: "\f179"; @fa-var-archive: "\f187"; @fa-var-area-chart: "\f1fe"; @fa-var-arrow-circle-down: "\f0ab"; @fa-var-arrow-circle-left: "\f0a8"; @fa-var-arrow-circle-o-down: "\f01a"; @fa-var-arrow-circle-o-left: "\f190"; @fa-var-arrow-circle-o-right: "\f18e"; @fa-var-arrow-circle-o-up: "\f01b"; @fa-var-arrow-circle-right: "\f0a9"; @fa-var-arrow-circle-up: "\f0aa"; @fa-var-arrow-down: "\f063"; @fa-var-arrow-left: "\f060"; @fa-var-arrow-right: "\f061"; @fa-var-arrow-up: "\f062"; @fa-var-arrows: "\f047"; @fa-var-arrows-alt: "\f0b2"; @fa-var-arrows-h: "\f07e"; @fa-var-arrows-v: "\f07d"; @fa-var-asl-interpreting: "\f2a3"; @fa-var-assistive-listening-systems: "\f2a2"; @fa-var-asterisk: "\f069"; @fa-var-at: "\f1fa"; @fa-var-audio-description: "\f29e"; @fa-var-automobile: "\f1b9"; @fa-var-backward: "\f04a"; @fa-var-balance-scale: "\f24e"; @fa-var-ban: "\f05e"; @fa-var-bandcamp: "\f2d5"; @fa-var-bank: "\f19c"; @fa-var-bar-chart: "\f080"; @fa-var-bar-chart-o: "\f080"; @fa-var-barcode: "\f02a"; @fa-var-bars: "\f0c9"; @fa-var-bath: "\f2cd"; @fa-var-bathtub: "\f2cd"; @fa-var-battery: "\f240"; @fa-var-battery-0: "\f244"; @fa-var-battery-1: "\f243"; @fa-var-battery-2: "\f242"; @fa-var-battery-3: "\f241"; @fa-var-battery-4: "\f240"; @fa-var-battery-empty: "\f244"; @fa-var-battery-full: "\f240"; @fa-var-battery-half: "\f242"; @fa-var-battery-quarter: "\f243"; @fa-var-battery-three-quarters: "\f241"; @fa-var-bed: "\f236"; @fa-var-beer: "\f0fc"; @fa-var-behance: "\f1b4"; @fa-var-behance-square: "\f1b5"; @fa-var-bell: "\f0f3"; @fa-var-bell-o: "\f0a2"; @fa-var-bell-slash: "\f1f6"; @fa-var-bell-slash-o: "\f1f7"; @fa-var-bicycle: "\f206"; @fa-var-binoculars: "\f1e5"; @fa-var-birthday-cake: "\f1fd"; @fa-var-bitbucket: "\f171"; @fa-var-bitbucket-square: "\f172"; @fa-var-bitcoin: "\f15a"; @fa-var-black-tie: "\f27e"; @fa-var-blind: "\f29d"; @fa-var-bluetooth: "\f293"; @fa-var-bluetooth-b: "\f294"; @fa-var-bold: "\f032"; @fa-var-bolt: "\f0e7"; @fa-var-bomb: "\f1e2"; @fa-var-book: "\f02d"; @fa-var-bookmark: "\f02e"; @fa-var-bookmark-o: "\f097"; @fa-var-braille: "\f2a1"; @fa-var-briefcase: "\f0b1"; @fa-var-btc: "\f15a"; @fa-var-bug: "\f188"; @fa-var-building: "\f1ad"; @fa-var-building-o: "\f0f7"; @fa-var-bullhorn: "\f0a1"; @fa-var-bullseye: "\f140"; @fa-var-bus: "\f207"; @fa-var-buysellads: "\f20d"; @fa-var-cab: "\f1ba"; @fa-var-calculator: "\f1ec"; @fa-var-calendar: "\f073"; @fa-var-calendar-check-o: "\f274"; @fa-var-calendar-minus-o: "\f272"; @fa-var-calendar-o: "\f133"; @fa-var-calendar-plus-o: "\f271"; @fa-var-calendar-times-o: "\f273"; @fa-var-camera: "\f030"; @fa-var-camera-retro: "\f083"; @fa-var-car: "\f1b9"; @fa-var-caret-down: "\f0d7"; @fa-var-caret-left: "\f0d9"; @fa-var-caret-right: "\f0da"; @fa-var-caret-square-o-down: "\f150"; @fa-var-caret-square-o-left: "\f191"; @fa-var-caret-square-o-right: "\f152"; @fa-var-caret-square-o-up: "\f151"; @fa-var-caret-up: "\f0d8"; @fa-var-cart-arrow-down: "\f218"; @fa-var-cart-plus: "\f217"; @fa-var-cc: "\f20a"; @fa-var-cc-amex: "\f1f3"; @fa-var-cc-diners-club: "\f24c"; @fa-var-cc-discover: "\f1f2"; @fa-var-cc-jcb: "\f24b"; @fa-var-cc-mastercard: "\f1f1"; @fa-var-cc-paypal: "\f1f4"; @fa-var-cc-stripe: "\f1f5"; @fa-var-cc-visa: "\f1f0"; @fa-var-certificate: "\f0a3"; @fa-var-chain: "\f0c1"; @fa-var-chain-broken: "\f127"; @fa-var-check: "\f00c"; @fa-var-check-circle: "\f058"; @fa-var-check-circle-o: "\f05d"; @fa-var-check-square: "\f14a"; @fa-var-check-square-o: "\f046"; @fa-var-chevron-circle-down: "\f13a"; @fa-var-chevron-circle-left: "\f137"; @fa-var-chevron-circle-right: "\f138"; @fa-var-chevron-circle-up: "\f139"; @fa-var-chevron-down: "\f078"; @fa-var-chevron-left: "\f053"; @fa-var-chevron-right: "\f054"; @fa-var-chevron-up: "\f077"; @fa-var-child: "\f1ae"; @fa-var-chrome: "\f268"; @fa-var-circle: "\f111"; @fa-var-circle-o: "\f10c"; @fa-var-circle-o-notch: "\f1ce"; @fa-var-circle-thin: "\f1db"; @fa-var-clipboard: "\f0ea"; @fa-var-clock-o: "\f017"; @fa-var-clone: "\f24d"; @fa-var-close: "\f00d"; @fa-var-cloud: "\f0c2"; @fa-var-cloud-download: "\f0ed"; @fa-var-cloud-upload: "\f0ee"; @fa-var-cny: "\f157"; @fa-var-code: "\f121"; @fa-var-code-fork: "\f126"; @fa-var-codepen: "\f1cb"; @fa-var-codiepie: "\f284"; @fa-var-coffee: "\f0f4"; @fa-var-cog: "\f013"; @fa-var-cogs: "\f085"; @fa-var-columns: "\f0db"; @fa-var-comment: "\f075"; @fa-var-comment-o: "\f0e5"; @fa-var-commenting: "\f27a"; @fa-var-commenting-o: "\f27b"; @fa-var-comments: "\f086"; @fa-var-comments-o: "\f0e6"; @fa-var-compass: "\f14e"; @fa-var-compress: "\f066"; @fa-var-connectdevelop: "\f20e"; @fa-var-contao: "\f26d"; @fa-var-copy: "\f0c5"; @fa-var-copyright: "\f1f9"; @fa-var-creative-commons: "\f25e"; @fa-var-credit-card: "\f09d"; @fa-var-credit-card-alt: "\f283"; @fa-var-crop: "\f125"; @fa-var-crosshairs: "\f05b"; @fa-var-css3: "\f13c"; @fa-var-cube: "\f1b2"; @fa-var-cubes: "\f1b3"; @fa-var-cut: "\f0c4"; @fa-var-cutlery: "\f0f5"; @fa-var-dashboard: "\f0e4"; @fa-var-dashcube: "\f210"; @fa-var-database: "\f1c0"; @fa-var-deaf: "\f2a4"; @fa-var-deafness: "\f2a4"; @fa-var-dedent: "\f03b"; @fa-var-delicious: "\f1a5"; @fa-var-desktop: "\f108"; @fa-var-deviantart: "\f1bd"; @fa-var-diamond: "\f219"; @fa-var-digg: "\f1a6"; @fa-var-dollar: "\f155"; @fa-var-dot-circle-o: "\f192"; @fa-var-download: "\f019"; @fa-var-dribbble: "\f17d"; @fa-var-drivers-license: "\f2c2"; @fa-var-drivers-license-o: "\f2c3"; @fa-var-dropbox: "\f16b"; @fa-var-drupal: "\f1a9"; @fa-var-edge: "\f282"; @fa-var-edit: "\f044"; @fa-var-eercast: "\f2da"; @fa-var-eject: "\f052"; @fa-var-ellipsis-h: "\f141"; @fa-var-ellipsis-v: "\f142"; @fa-var-empire: "\f1d1"; @fa-var-envelope: "\f0e0"; @fa-var-envelope-o: "\f003"; @fa-var-envelope-open: "\f2b6"; @fa-var-envelope-open-o: "\f2b7"; @fa-var-envelope-square: "\f199"; @fa-var-envira: "\f299"; @fa-var-eraser: "\f12d"; @fa-var-etsy: "\f2d7"; @fa-var-eur: "\f153"; @fa-var-euro: "\f153"; @fa-var-exchange: "\f0ec"; @fa-var-exclamation: "\f12a"; @fa-var-exclamation-circle: "\f06a"; @fa-var-exclamation-triangle: "\f071"; @fa-var-expand: "\f065"; @fa-var-expeditedssl: "\f23e"; @fa-var-external-link: "\f08e"; @fa-var-external-link-square: "\f14c"; @fa-var-eye: "\f06e"; @fa-var-eye-slash: "\f070"; @fa-var-eyedropper: "\f1fb"; @fa-var-fa: "\f2b4"; @fa-var-facebook: "\f09a"; @fa-var-facebook-f: "\f09a"; @fa-var-facebook-official: "\f230"; @fa-var-facebook-square: "\f082"; @fa-var-fast-backward: "\f049"; @fa-var-fast-forward: "\f050"; @fa-var-fax: "\f1ac"; @fa-var-feed: "\f09e"; @fa-var-female: "\f182"; @fa-var-fighter-jet: "\f0fb"; @fa-var-file: "\f15b"; @fa-var-file-archive-o: "\f1c6"; @fa-var-file-audio-o: "\f1c7"; @fa-var-file-code-o: "\f1c9"; @fa-var-file-excel-o: "\f1c3"; @fa-var-file-image-o: "\f1c5"; @fa-var-file-movie-o: "\f1c8"; @fa-var-file-o: "\f016"; @fa-var-file-pdf-o: "\f1c1"; @fa-var-file-photo-o: "\f1c5"; @fa-var-file-picture-o: "\f1c5"; @fa-var-file-powerpoint-o: "\f1c4"; @fa-var-file-sound-o: "\f1c7"; @fa-var-file-text: "\f15c"; @fa-var-file-text-o: "\f0f6"; @fa-var-file-video-o: "\f1c8"; @fa-var-file-word-o: "\f1c2"; @fa-var-file-zip-o: "\f1c6"; @fa-var-files-o: "\f0c5"; @fa-var-film: "\f008"; @fa-var-filter: "\f0b0"; @fa-var-fire: "\f06d"; @fa-var-fire-extinguisher: "\f134"; @fa-var-firefox: "\f269"; @fa-var-first-order: "\f2b0"; @fa-var-flag: "\f024"; @fa-var-flag-checkered: "\f11e"; @fa-var-flag-o: "\f11d"; @fa-var-flash: "\f0e7"; @fa-var-flask: "\f0c3"; @fa-var-flickr: "\f16e"; @fa-var-floppy-o: "\f0c7"; @fa-var-folder: "\f07b"; @fa-var-folder-o: "\f114"; @fa-var-folder-open: "\f07c"; @fa-var-folder-open-o: "\f115"; @fa-var-font: "\f031"; @fa-var-font-awesome: "\f2b4"; @fa-var-fonticons: "\f280"; @fa-var-fort-awesome: "\f286"; @fa-var-forumbee: "\f211"; @fa-var-forward: "\f04e"; @fa-var-foursquare: "\f180"; @fa-var-free-code-camp: "\f2c5"; @fa-var-frown-o: "\f119"; @fa-var-futbol-o: "\f1e3"; @fa-var-gamepad: "\f11b"; @fa-var-gavel: "\f0e3"; @fa-var-gbp: "\f154"; @fa-var-ge: "\f1d1"; @fa-var-gear: "\f013"; @fa-var-gears: "\f085"; @fa-var-genderless: "\f22d"; @fa-var-get-pocket: "\f265"; @fa-var-gg: "\f260"; @fa-var-gg-circle: "\f261"; @fa-var-gift: "\f06b"; @fa-var-git: "\f1d3"; @fa-var-git-square: "\f1d2"; @fa-var-github: "\f09b"; @fa-var-github-alt: "\f113"; @fa-var-github-square: "\f092"; @fa-var-gitlab: "\f296"; @fa-var-gittip: "\f184"; @fa-var-glass: "\f000"; @fa-var-glide: "\f2a5"; @fa-var-glide-g: "\f2a6"; @fa-var-globe: "\f0ac"; @fa-var-google: "\f1a0"; @fa-var-google-plus: "\f0d5"; @fa-var-google-plus-circle: "\f2b3"; @fa-var-google-plus-official: "\f2b3"; @fa-var-google-plus-square: "\f0d4"; @fa-var-google-wallet: "\f1ee"; @fa-var-graduation-cap: "\f19d"; @fa-var-gratipay: "\f184"; @fa-var-grav: "\f2d6"; @fa-var-group: "\f0c0"; @fa-var-h-square: "\f0fd"; @fa-var-hacker-news: "\f1d4"; @fa-var-hand-grab-o: "\f255"; @fa-var-hand-lizard-o: "\f258"; @fa-var-hand-o-down: "\f0a7"; @fa-var-hand-o-left: "\f0a5"; @fa-var-hand-o-right: "\f0a4"; @fa-var-hand-o-up: "\f0a6"; @fa-var-hand-paper-o: "\f256"; @fa-var-hand-peace-o: "\f25b"; @fa-var-hand-pointer-o: "\f25a"; @fa-var-hand-rock-o: "\f255"; @fa-var-hand-scissors-o: "\f257"; @fa-var-hand-spock-o: "\f259"; @fa-var-hand-stop-o: "\f256"; @fa-var-handshake-o: "\f2b5"; @fa-var-hard-of-hearing: "\f2a4"; @fa-var-hashtag: "\f292"; @fa-var-hdd-o: "\f0a0"; @fa-var-header: "\f1dc"; @fa-var-headphones: "\f025"; @fa-var-heart: "\f004"; @fa-var-heart-o: "\f08a"; @fa-var-heartbeat: "\f21e"; @fa-var-history: "\f1da"; @fa-var-home: "\f015"; @fa-var-hospital-o: "\f0f8"; @fa-var-hotel: "\f236"; @fa-var-hourglass: "\f254"; @fa-var-hourglass-1: "\f251"; @fa-var-hourglass-2: "\f252"; @fa-var-hourglass-3: "\f253"; @fa-var-hourglass-end: "\f253"; @fa-var-hourglass-half: "\f252"; @fa-var-hourglass-o: "\f250"; @fa-var-hourglass-start: "\f251"; @fa-var-houzz: "\f27c"; @fa-var-html5: "\f13b"; @fa-var-i-cursor: "\f246"; @fa-var-id-badge: "\f2c1"; @fa-var-id-card: "\f2c2"; @fa-var-id-card-o: "\f2c3"; @fa-var-ils: "\f20b"; @fa-var-image: "\f03e"; @fa-var-imdb: "\f2d8"; @fa-var-inbox: "\f01c"; @fa-var-indent: "\f03c"; @fa-var-industry: "\f275"; @fa-var-info: "\f129"; @fa-var-info-circle: "\f05a"; @fa-var-inr: "\f156"; @fa-var-instagram: "\f16d"; @fa-var-institution: "\f19c"; @fa-var-internet-explorer: "\f26b"; @fa-var-intersex: "\f224"; @fa-var-ioxhost: "\f208"; @fa-var-italic: "\f033"; @fa-var-joomla: "\f1aa"; @fa-var-jpy: "\f157"; @fa-var-jsfiddle: "\f1cc"; @fa-var-key: "\f084"; @fa-var-keyboard-o: "\f11c"; @fa-var-krw: "\f159"; @fa-var-language: "\f1ab"; @fa-var-laptop: "\f109"; @fa-var-lastfm: "\f202"; @fa-var-lastfm-square: "\f203"; @fa-var-leaf: "\f06c"; @fa-var-leanpub: "\f212"; @fa-var-legal: "\f0e3"; @fa-var-lemon-o: "\f094"; @fa-var-level-down: "\f149"; @fa-var-level-up: "\f148"; @fa-var-life-bouy: "\f1cd"; @fa-var-life-buoy: "\f1cd"; @fa-var-life-ring: "\f1cd"; @fa-var-life-saver: "\f1cd"; @fa-var-lightbulb-o: "\f0eb"; @fa-var-line-chart: "\f201"; @fa-var-link: "\f0c1"; @fa-var-linkedin: "\f0e1"; @fa-var-linkedin-square: "\f08c"; @fa-var-linode: "\f2b8"; @fa-var-linux: "\f17c"; @fa-var-list: "\f03a"; @fa-var-list-alt: "\f022"; @fa-var-list-ol: "\f0cb"; @fa-var-list-ul: "\f0ca"; @fa-var-location-arrow: "\f124"; @fa-var-lock: "\f023"; @fa-var-long-arrow-down: "\f175"; @fa-var-long-arrow-left: "\f177"; @fa-var-long-arrow-right: "\f178"; @fa-var-long-arrow-up: "\f176"; @fa-var-low-vision: "\f2a8"; @fa-var-magic: "\f0d0"; @fa-var-magnet: "\f076"; @fa-var-mail-forward: "\f064"; @fa-var-mail-reply: "\f112"; @fa-var-mail-reply-all: "\f122"; @fa-var-male: "\f183"; @fa-var-map: "\f279"; @fa-var-map-marker: "\f041"; @fa-var-map-o: "\f278"; @fa-var-map-pin: "\f276"; @fa-var-map-signs: "\f277"; @fa-var-mars: "\f222"; @fa-var-mars-double: "\f227"; @fa-var-mars-stroke: "\f229"; @fa-var-mars-stroke-h: "\f22b"; @fa-var-mars-stroke-v: "\f22a"; @fa-var-maxcdn: "\f136"; @fa-var-meanpath: "\f20c"; @fa-var-medium: "\f23a"; @fa-var-medkit: "\f0fa"; @fa-var-meetup: "\f2e0"; @fa-var-meh-o: "\f11a"; @fa-var-mercury: "\f223"; @fa-var-microchip: "\f2db"; @fa-var-microphone: "\f130"; @fa-var-microphone-slash: "\f131"; @fa-var-minus: "\f068"; @fa-var-minus-circle: "\f056"; @fa-var-minus-square: "\f146"; @fa-var-minus-square-o: "\f147"; @fa-var-mixcloud: "\f289"; @fa-var-mobile: "\f10b"; @fa-var-mobile-phone: "\f10b"; @fa-var-modx: "\f285"; @fa-var-money: "\f0d6"; @fa-var-moon-o: "\f186"; @fa-var-mortar-board: "\f19d"; @fa-var-motorcycle: "\f21c"; @fa-var-mouse-pointer: "\f245"; @fa-var-music: "\f001"; @fa-var-navicon: "\f0c9"; @fa-var-neuter: "\f22c"; @fa-var-newspaper-o: "\f1ea"; @fa-var-object-group: "\f247"; @fa-var-object-ungroup: "\f248"; @fa-var-odnoklassniki: "\f263"; @fa-var-odnoklassniki-square: "\f264"; @fa-var-opencart: "\f23d"; @fa-var-openid: "\f19b"; @fa-var-opera: "\f26a"; @fa-var-optin-monster: "\f23c"; @fa-var-outdent: "\f03b"; @fa-var-pagelines: "\f18c"; @fa-var-paint-brush: "\f1fc"; @fa-var-paper-plane: "\f1d8"; @fa-var-paper-plane-o: "\f1d9"; @fa-var-paperclip: "\f0c6"; @fa-var-paragraph: "\f1dd"; @fa-var-paste: "\f0ea"; @fa-var-pause: "\f04c"; @fa-var-pause-circle: "\f28b"; @fa-var-pause-circle-o: "\f28c"; @fa-var-paw: "\f1b0"; @fa-var-paypal: "\f1ed"; @fa-var-pencil: "\f040"; @fa-var-pencil-square: "\f14b"; @fa-var-pencil-square-o: "\f044"; @fa-var-percent: "\f295"; @fa-var-phone: "\f095"; @fa-var-phone-square: "\f098"; @fa-var-photo: "\f03e"; @fa-var-picture-o: "\f03e"; @fa-var-pie-chart: "\f200"; @fa-var-pied-piper: "\f2ae"; @fa-var-pied-piper-alt: "\f1a8"; @fa-var-pied-piper-pp: "\f1a7"; @fa-var-pinterest: "\f0d2"; @fa-var-pinterest-p: "\f231"; @fa-var-pinterest-square: "\f0d3"; @fa-var-plane: "\f072"; @fa-var-play: "\f04b"; @fa-var-play-circle: "\f144"; @fa-var-play-circle-o: "\f01d"; @fa-var-plug: "\f1e6"; @fa-var-plus: "\f067"; @fa-var-plus-circle: "\f055"; @fa-var-plus-square: "\f0fe"; @fa-var-plus-square-o: "\f196"; @fa-var-podcast: "\f2ce"; @fa-var-power-off: "\f011"; @fa-var-print: "\f02f"; @fa-var-product-hunt: "\f288"; @fa-var-puzzle-piece: "\f12e"; @fa-var-qq: "\f1d6"; @fa-var-qrcode: "\f029"; @fa-var-question: "\f128"; @fa-var-question-circle: "\f059"; @fa-var-question-circle-o: "\f29c"; @fa-var-quora: "\f2c4"; @fa-var-quote-left: "\f10d"; @fa-var-quote-right: "\f10e"; @fa-var-ra: "\f1d0"; @fa-var-random: "\f074"; @fa-var-ravelry: "\f2d9"; @fa-var-rebel: "\f1d0"; @fa-var-recycle: "\f1b8"; @fa-var-reddit: "\f1a1"; @fa-var-reddit-alien: "\f281"; @fa-var-reddit-square: "\f1a2"; @fa-var-refresh: "\f021"; @fa-var-registered: "\f25d"; @fa-var-remove: "\f00d"; @fa-var-renren: "\f18b"; @fa-var-reorder: "\f0c9"; @fa-var-repeat: "\f01e"; @fa-var-reply: "\f112"; @fa-var-reply-all: "\f122"; @fa-var-resistance: "\f1d0"; @fa-var-retweet: "\f079"; @fa-var-rmb: "\f157"; @fa-var-road: "\f018"; @fa-var-rocket: "\f135"; @fa-var-rotate-left: "\f0e2"; @fa-var-rotate-right: "\f01e"; @fa-var-rouble: "\f158"; @fa-var-rss: "\f09e"; @fa-var-rss-square: "\f143"; @fa-var-rub: "\f158"; @fa-var-ruble: "\f158"; @fa-var-rupee: "\f156"; @fa-var-s15: "\f2cd"; @fa-var-safari: "\f267"; @fa-var-save: "\f0c7"; @fa-var-scissors: "\f0c4"; @fa-var-scribd: "\f28a"; @fa-var-search: "\f002"; @fa-var-search-minus: "\f010"; @fa-var-search-plus: "\f00e"; @fa-var-sellsy: "\f213"; @fa-var-send: "\f1d8"; @fa-var-send-o: "\f1d9"; @fa-var-server: "\f233"; @fa-var-share: "\f064"; @fa-var-share-alt: "\f1e0"; @fa-var-share-alt-square: "\f1e1"; @fa-var-share-square: "\f14d"; @fa-var-share-square-o: "\f045"; @fa-var-shekel: "\f20b"; @fa-var-sheqel: "\f20b"; @fa-var-shield: "\f132"; @fa-var-ship: "\f21a"; @fa-var-shirtsinbulk: "\f214"; @fa-var-shopping-bag: "\f290"; @fa-var-shopping-basket: "\f291"; @fa-var-shopping-cart: "\f07a"; @fa-var-shower: "\f2cc"; @fa-var-sign-in: "\f090"; @fa-var-sign-language: "\f2a7"; @fa-var-sign-out: "\f08b"; @fa-var-signal: "\f012"; @fa-var-signing: "\f2a7"; @fa-var-simplybuilt: "\f215"; @fa-var-sitemap: "\f0e8"; @fa-var-skyatlas: "\f216"; @fa-var-skype: "\f17e"; @fa-var-slack: "\f198"; @fa-var-sliders: "\f1de"; @fa-var-slideshare: "\f1e7"; @fa-var-smile-o: "\f118"; @fa-var-snapchat: "\f2ab"; @fa-var-snapchat-ghost: "\f2ac"; @fa-var-snapchat-square: "\f2ad"; @fa-var-snowflake-o: "\f2dc"; @fa-var-soccer-ball-o: "\f1e3"; @fa-var-sort: "\f0dc"; @fa-var-sort-alpha-asc: "\f15d"; @fa-var-sort-alpha-desc: "\f15e"; @fa-var-sort-amount-asc: "\f160"; @fa-var-sort-amount-desc: "\f161"; @fa-var-sort-asc: "\f0de"; @fa-var-sort-desc: "\f0dd"; @fa-var-sort-down: "\f0dd"; @fa-var-sort-numeric-asc: "\f162"; @fa-var-sort-numeric-desc: "\f163"; @fa-var-sort-up: "\f0de"; @fa-var-soundcloud: "\f1be"; @fa-var-space-shuttle: "\f197"; @fa-var-spinner: "\f110"; @fa-var-spoon: "\f1b1"; @fa-var-spotify: "\f1bc"; @fa-var-square: "\f0c8"; @fa-var-square-o: "\f096"; @fa-var-stack-exchange: "\f18d"; @fa-var-stack-overflow: "\f16c"; @fa-var-star: "\f005"; @fa-var-star-half: "\f089"; @fa-var-star-half-empty: "\f123"; @fa-var-star-half-full: "\f123"; @fa-var-star-half-o: "\f123"; @fa-var-star-o: "\f006"; @fa-var-steam: "\f1b6"; @fa-var-steam-square: "\f1b7"; @fa-var-step-backward: "\f048"; @fa-var-step-forward: "\f051"; @fa-var-stethoscope: "\f0f1"; @fa-var-sticky-note: "\f249"; @fa-var-sticky-note-o: "\f24a"; @fa-var-stop: "\f04d"; @fa-var-stop-circle: "\f28d"; @fa-var-stop-circle-o: "\f28e"; @fa-var-street-view: "\f21d"; @fa-var-strikethrough: "\f0cc"; @fa-var-stumbleupon: "\f1a4"; @fa-var-stumbleupon-circle: "\f1a3"; @fa-var-subscript: "\f12c"; @fa-var-subway: "\f239"; @fa-var-suitcase: "\f0f2"; @fa-var-sun-o: "\f185"; @fa-var-superpowers: "\f2dd"; @fa-var-superscript: "\f12b"; @fa-var-support: "\f1cd"; @fa-var-table: "\f0ce"; @fa-var-tablet: "\f10a"; @fa-var-tachometer: "\f0e4"; @fa-var-tag: "\f02b"; @fa-var-tags: "\f02c"; @fa-var-tasks: "\f0ae"; @fa-var-taxi: "\f1ba"; @fa-var-telegram: "\f2c6"; @fa-var-television: "\f26c"; @fa-var-tencent-weibo: "\f1d5"; @fa-var-terminal: "\f120"; @fa-var-text-height: "\f034"; @fa-var-text-width: "\f035"; @fa-var-th: "\f00a"; @fa-var-th-large: "\f009"; @fa-var-th-list: "\f00b"; @fa-var-themeisle: "\f2b2"; @fa-var-thermometer: "\f2c7"; @fa-var-thermometer-0: "\f2cb"; @fa-var-thermometer-1: "\f2ca"; @fa-var-thermometer-2: "\f2c9"; @fa-var-thermometer-3: "\f2c8"; @fa-var-thermometer-4: "\f2c7"; @fa-var-thermometer-empty: "\f2cb"; @fa-var-thermometer-full: "\f2c7"; @fa-var-thermometer-half: "\f2c9"; @fa-var-thermometer-quarter: "\f2ca"; @fa-var-thermometer-three-quarters: "\f2c8"; @fa-var-thumb-tack: "\f08d"; @fa-var-thumbs-down: "\f165"; @fa-var-thumbs-o-down: "\f088"; @fa-var-thumbs-o-up: "\f087"; @fa-var-thumbs-up: "\f164"; @fa-var-ticket: "\f145"; @fa-var-times: "\f00d"; @fa-var-times-circle: "\f057"; @fa-var-times-circle-o: "\f05c"; @fa-var-times-rectangle: "\f2d3"; @fa-var-times-rectangle-o: "\f2d4"; @fa-var-tint: "\f043"; @fa-var-toggle-down: "\f150"; @fa-var-toggle-left: "\f191"; @fa-var-toggle-off: "\f204"; @fa-var-toggle-on: "\f205"; @fa-var-toggle-right: "\f152"; @fa-var-toggle-up: "\f151"; @fa-var-trademark: "\f25c"; @fa-var-train: "\f238"; @fa-var-transgender: "\f224"; @fa-var-transgender-alt: "\f225"; @fa-var-trash: "\f1f8"; @fa-var-trash-o: "\f014"; @fa-var-tree: "\f1bb"; @fa-var-trello: "\f181"; @fa-var-tripadvisor: "\f262"; @fa-var-trophy: "\f091"; @fa-var-truck: "\f0d1"; @fa-var-try: "\f195"; @fa-var-tty: "\f1e4"; @fa-var-tumblr: "\f173"; @fa-var-tumblr-square: "\f174"; @fa-var-turkish-lira: "\f195"; @fa-var-tv: "\f26c"; @fa-var-twitch: "\f1e8"; @fa-var-twitter: "\f099"; @fa-var-twitter-square: "\f081"; @fa-var-umbrella: "\f0e9"; @fa-var-underline: "\f0cd"; @fa-var-undo: "\f0e2"; @fa-var-universal-access: "\f29a"; @fa-var-university: "\f19c"; @fa-var-unlink: "\f127"; @fa-var-unlock: "\f09c"; @fa-var-unlock-alt: "\f13e"; @fa-var-unsorted: "\f0dc"; @fa-var-upload: "\f093"; @fa-var-usb: "\f287"; @fa-var-usd: "\f155"; @fa-var-user: "\f007"; @fa-var-user-circle: "\f2bd"; @fa-var-user-circle-o: "\f2be"; @fa-var-user-md: "\f0f0"; @fa-var-user-o: "\f2c0"; @fa-var-user-plus: "\f234"; @fa-var-user-secret: "\f21b"; @fa-var-user-times: "\f235"; @fa-var-users: "\f0c0"; @fa-var-vcard: "\f2bb"; @fa-var-vcard-o: "\f2bc"; @fa-var-venus: "\f221"; @fa-var-venus-double: "\f226"; @fa-var-venus-mars: "\f228"; @fa-var-viacoin: "\f237"; @fa-var-viadeo: "\f2a9"; @fa-var-viadeo-square: "\f2aa"; @fa-var-video-camera: "\f03d"; @fa-var-vimeo: "\f27d"; @fa-var-vimeo-square: "\f194"; @fa-var-vine: "\f1ca"; @fa-var-vk: "\f189"; @fa-var-volume-control-phone: "\f2a0"; @fa-var-volume-down: "\f027"; @fa-var-volume-off: "\f026"; @fa-var-volume-up: "\f028"; @fa-var-warning: "\f071"; @fa-var-wechat: "\f1d7"; @fa-var-weibo: "\f18a"; @fa-var-weixin: "\f1d7"; @fa-var-whatsapp: "\f232"; @fa-var-wheelchair: "\f193"; @fa-var-wheelchair-alt: "\f29b"; @fa-var-wifi: "\f1eb"; @fa-var-wikipedia-w: "\f266"; @fa-var-window-close: "\f2d3"; @fa-var-window-close-o: "\f2d4"; @fa-var-window-maximize: "\f2d0"; @fa-var-window-minimize: "\f2d1"; @fa-var-window-restore: "\f2d2"; @fa-var-windows: "\f17a"; @fa-var-won: "\f159"; @fa-var-wordpress: "\f19a"; @fa-var-wpbeginner: "\f297"; @fa-var-wpexplorer: "\f2de"; @fa-var-wpforms: "\f298"; @fa-var-wrench: "\f0ad"; @fa-var-xing: "\f168"; @fa-var-xing-square: "\f169"; @fa-var-y-combinator: "\f23b"; @fa-var-y-combinator-square: "\f1d4"; @fa-var-yahoo: "\f19e"; @fa-var-yc: "\f23b"; @fa-var-yc-square: "\f1d4"; @fa-var-yelp: "\f1e9"; @fa-var-yen: "\f157"; @fa-var-yoast: "\f2b1"; @fa-var-youtube: "\f167"; @fa-var-youtube-play: "\f16a"; @fa-var-youtube-square: "\f166"; ================================================ FILE: static/font-awesome-4.7.0/scss/_animated.scss ================================================ // Spinning Icons // -------------------------- .#{$fa-css-prefix}-spin { -webkit-animation: fa-spin 2s infinite linear; animation: fa-spin 2s infinite linear; } .#{$fa-css-prefix}-pulse { -webkit-animation: fa-spin 1s infinite steps(8); animation: fa-spin 1s infinite steps(8); } @-webkit-keyframes fa-spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg); } } @keyframes fa-spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg); } } ================================================ FILE: static/font-awesome-4.7.0/scss/_bordered-pulled.scss ================================================ // Bordered & Pulled // ------------------------- .#{$fa-css-prefix}-border { padding: .2em .25em .15em; border: solid .08em $fa-border-color; border-radius: .1em; } .#{$fa-css-prefix}-pull-left { float: left; } .#{$fa-css-prefix}-pull-right { float: right; } .#{$fa-css-prefix} { &.#{$fa-css-prefix}-pull-left { margin-right: .3em; } &.#{$fa-css-prefix}-pull-right { margin-left: .3em; } } /* Deprecated as of 4.4.0 */ .pull-right { float: right; } .pull-left { float: left; } .#{$fa-css-prefix} { &.pull-left { margin-right: .3em; } &.pull-right { margin-left: .3em; } } ================================================ FILE: static/font-awesome-4.7.0/scss/_core.scss ================================================ // Base Class Definition // ------------------------- .#{$fa-css-prefix} { display: inline-block; font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration font-size: inherit; // can't have font-size inherit on line above, so need to override text-rendering: auto; // optimizelegibility throws things off #1094 -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } ================================================ FILE: static/font-awesome-4.7.0/scss/_fixed-width.scss ================================================ // Fixed Width Icons // ------------------------- .#{$fa-css-prefix}-fw { width: (18em / 14); text-align: center; } ================================================ FILE: static/font-awesome-4.7.0/scss/_icons.scss ================================================ /* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen readers do not read off random characters that represent icons */ .#{$fa-css-prefix}-glass:before { content: $fa-var-glass; } .#{$fa-css-prefix}-music:before { content: $fa-var-music; } .#{$fa-css-prefix}-search:before { content: $fa-var-search; } .#{$fa-css-prefix}-envelope-o:before { content: $fa-var-envelope-o; } .#{$fa-css-prefix}-heart:before { content: $fa-var-heart; } .#{$fa-css-prefix}-star:before { content: $fa-var-star; } .#{$fa-css-prefix}-star-o:before { content: $fa-var-star-o; } .#{$fa-css-prefix}-user:before { content: $fa-var-user; } .#{$fa-css-prefix}-film:before { content: $fa-var-film; } .#{$fa-css-prefix}-th-large:before { content: $fa-var-th-large; } .#{$fa-css-prefix}-th:before { content: $fa-var-th; } .#{$fa-css-prefix}-th-list:before { content: $fa-var-th-list; } .#{$fa-css-prefix}-check:before { content: $fa-var-check; } .#{$fa-css-prefix}-remove:before, .#{$fa-css-prefix}-close:before, .#{$fa-css-prefix}-times:before { content: $fa-var-times; } .#{$fa-css-prefix}-search-plus:before { content: $fa-var-search-plus; } .#{$fa-css-prefix}-search-minus:before { content: $fa-var-search-minus; } .#{$fa-css-prefix}-power-off:before { content: $fa-var-power-off; } .#{$fa-css-prefix}-signal:before { content: $fa-var-signal; } .#{$fa-css-prefix}-gear:before, .#{$fa-css-prefix}-cog:before { content: $fa-var-cog; } .#{$fa-css-prefix}-trash-o:before { content: $fa-var-trash-o; } .#{$fa-css-prefix}-home:before { content: $fa-var-home; } .#{$fa-css-prefix}-file-o:before { content: $fa-var-file-o; } .#{$fa-css-prefix}-clock-o:before { content: $fa-var-clock-o; } .#{$fa-css-prefix}-road:before { content: $fa-var-road; } .#{$fa-css-prefix}-download:before { content: $fa-var-download; } .#{$fa-css-prefix}-arrow-circle-o-down:before { content: $fa-var-arrow-circle-o-down; } .#{$fa-css-prefix}-arrow-circle-o-up:before { content: $fa-var-arrow-circle-o-up; } .#{$fa-css-prefix}-inbox:before { content: $fa-var-inbox; } .#{$fa-css-prefix}-play-circle-o:before { content: $fa-var-play-circle-o; } .#{$fa-css-prefix}-rotate-right:before, .#{$fa-css-prefix}-repeat:before { content: $fa-var-repeat; } .#{$fa-css-prefix}-refresh:before { content: $fa-var-refresh; } .#{$fa-css-prefix}-list-alt:before { content: $fa-var-list-alt; } .#{$fa-css-prefix}-lock:before { content: $fa-var-lock; } .#{$fa-css-prefix}-flag:before { content: $fa-var-flag; } .#{$fa-css-prefix}-headphones:before { content: $fa-var-headphones; } .#{$fa-css-prefix}-volume-off:before { content: $fa-var-volume-off; } .#{$fa-css-prefix}-volume-down:before { content: $fa-var-volume-down; } .#{$fa-css-prefix}-volume-up:before { content: $fa-var-volume-up; } .#{$fa-css-prefix}-qrcode:before { content: $fa-var-qrcode; } .#{$fa-css-prefix}-barcode:before { content: $fa-var-barcode; } .#{$fa-css-prefix}-tag:before { content: $fa-var-tag; } .#{$fa-css-prefix}-tags:before { content: $fa-var-tags; } .#{$fa-css-prefix}-book:before { content: $fa-var-book; } .#{$fa-css-prefix}-bookmark:before { content: $fa-var-bookmark; } .#{$fa-css-prefix}-print:before { content: $fa-var-print; } .#{$fa-css-prefix}-camera:before { content: $fa-var-camera; } .#{$fa-css-prefix}-font:before { content: $fa-var-font; } .#{$fa-css-prefix}-bold:before { content: $fa-var-bold; } .#{$fa-css-prefix}-italic:before { content: $fa-var-italic; } .#{$fa-css-prefix}-text-height:before { content: $fa-var-text-height; } .#{$fa-css-prefix}-text-width:before { content: $fa-var-text-width; } .#{$fa-css-prefix}-align-left:before { content: $fa-var-align-left; } .#{$fa-css-prefix}-align-center:before { content: $fa-var-align-center; } .#{$fa-css-prefix}-align-right:before { content: $fa-var-align-right; } .#{$fa-css-prefix}-align-justify:before { content: $fa-var-align-justify; } .#{$fa-css-prefix}-list:before { content: $fa-var-list; } .#{$fa-css-prefix}-dedent:before, .#{$fa-css-prefix}-outdent:before { content: $fa-var-outdent; } .#{$fa-css-prefix}-indent:before { content: $fa-var-indent; } .#{$fa-css-prefix}-video-camera:before { content: $fa-var-video-camera; } .#{$fa-css-prefix}-photo:before, .#{$fa-css-prefix}-image:before, .#{$fa-css-prefix}-picture-o:before { content: $fa-var-picture-o; } .#{$fa-css-prefix}-pencil:before { content: $fa-var-pencil; } .#{$fa-css-prefix}-map-marker:before { content: $fa-var-map-marker; } .#{$fa-css-prefix}-adjust:before { content: $fa-var-adjust; } .#{$fa-css-prefix}-tint:before { content: $fa-var-tint; } .#{$fa-css-prefix}-edit:before, .#{$fa-css-prefix}-pencil-square-o:before { content: $fa-var-pencil-square-o; } .#{$fa-css-prefix}-share-square-o:before { content: $fa-var-share-square-o; } .#{$fa-css-prefix}-check-square-o:before { content: $fa-var-check-square-o; } .#{$fa-css-prefix}-arrows:before { content: $fa-var-arrows; } .#{$fa-css-prefix}-step-backward:before { content: $fa-var-step-backward; } .#{$fa-css-prefix}-fast-backward:before { content: $fa-var-fast-backward; } .#{$fa-css-prefix}-backward:before { content: $fa-var-backward; } .#{$fa-css-prefix}-play:before { content: $fa-var-play; } .#{$fa-css-prefix}-pause:before { content: $fa-var-pause; } .#{$fa-css-prefix}-stop:before { content: $fa-var-stop; } .#{$fa-css-prefix}-forward:before { content: $fa-var-forward; } .#{$fa-css-prefix}-fast-forward:before { content: $fa-var-fast-forward; } .#{$fa-css-prefix}-step-forward:before { content: $fa-var-step-forward; } .#{$fa-css-prefix}-eject:before { content: $fa-var-eject; } .#{$fa-css-prefix}-chevron-left:before { content: $fa-var-chevron-left; } .#{$fa-css-prefix}-chevron-right:before { content: $fa-var-chevron-right; } .#{$fa-css-prefix}-plus-circle:before { content: $fa-var-plus-circle; } .#{$fa-css-prefix}-minus-circle:before { content: $fa-var-minus-circle; } .#{$fa-css-prefix}-times-circle:before { content: $fa-var-times-circle; } .#{$fa-css-prefix}-check-circle:before { content: $fa-var-check-circle; } .#{$fa-css-prefix}-question-circle:before { content: $fa-var-question-circle; } .#{$fa-css-prefix}-info-circle:before { content: $fa-var-info-circle; } .#{$fa-css-prefix}-crosshairs:before { content: $fa-var-crosshairs; } .#{$fa-css-prefix}-times-circle-o:before { content: $fa-var-times-circle-o; } .#{$fa-css-prefix}-check-circle-o:before { content: $fa-var-check-circle-o; } .#{$fa-css-prefix}-ban:before { content: $fa-var-ban; } .#{$fa-css-prefix}-arrow-left:before { content: $fa-var-arrow-left; } .#{$fa-css-prefix}-arrow-right:before { content: $fa-var-arrow-right; } .#{$fa-css-prefix}-arrow-up:before { content: $fa-var-arrow-up; } .#{$fa-css-prefix}-arrow-down:before { content: $fa-var-arrow-down; } .#{$fa-css-prefix}-mail-forward:before, .#{$fa-css-prefix}-share:before { content: $fa-var-share; } .#{$fa-css-prefix}-expand:before { content: $fa-var-expand; } .#{$fa-css-prefix}-compress:before { content: $fa-var-compress; } .#{$fa-css-prefix}-plus:before { content: $fa-var-plus; } .#{$fa-css-prefix}-minus:before { content: $fa-var-minus; } .#{$fa-css-prefix}-asterisk:before { content: $fa-var-asterisk; } .#{$fa-css-prefix}-exclamation-circle:before { content: $fa-var-exclamation-circle; } .#{$fa-css-prefix}-gift:before { content: $fa-var-gift; } .#{$fa-css-prefix}-leaf:before { content: $fa-var-leaf; } .#{$fa-css-prefix}-fire:before { content: $fa-var-fire; } .#{$fa-css-prefix}-eye:before { content: $fa-var-eye; } .#{$fa-css-prefix}-eye-slash:before { content: $fa-var-eye-slash; } .#{$fa-css-prefix}-warning:before, .#{$fa-css-prefix}-exclamation-triangle:before { content: $fa-var-exclamation-triangle; } .#{$fa-css-prefix}-plane:before { content: $fa-var-plane; } .#{$fa-css-prefix}-calendar:before { content: $fa-var-calendar; } .#{$fa-css-prefix}-random:before { content: $fa-var-random; } .#{$fa-css-prefix}-comment:before { content: $fa-var-comment; } .#{$fa-css-prefix}-magnet:before { content: $fa-var-magnet; } .#{$fa-css-prefix}-chevron-up:before { content: $fa-var-chevron-up; } .#{$fa-css-prefix}-chevron-down:before { content: $fa-var-chevron-down; } .#{$fa-css-prefix}-retweet:before { content: $fa-var-retweet; } .#{$fa-css-prefix}-shopping-cart:before { content: $fa-var-shopping-cart; } .#{$fa-css-prefix}-folder:before { content: $fa-var-folder; } .#{$fa-css-prefix}-folder-open:before { content: $fa-var-folder-open; } .#{$fa-css-prefix}-arrows-v:before { content: $fa-var-arrows-v; } .#{$fa-css-prefix}-arrows-h:before { content: $fa-var-arrows-h; } .#{$fa-css-prefix}-bar-chart-o:before, .#{$fa-css-prefix}-bar-chart:before { content: $fa-var-bar-chart; } .#{$fa-css-prefix}-twitter-square:before { content: $fa-var-twitter-square; } .#{$fa-css-prefix}-facebook-square:before { content: $fa-var-facebook-square; } .#{$fa-css-prefix}-camera-retro:before { content: $fa-var-camera-retro; } .#{$fa-css-prefix}-key:before { content: $fa-var-key; } .#{$fa-css-prefix}-gears:before, .#{$fa-css-prefix}-cogs:before { content: $fa-var-cogs; } .#{$fa-css-prefix}-comments:before { content: $fa-var-comments; } .#{$fa-css-prefix}-thumbs-o-up:before { content: $fa-var-thumbs-o-up; } .#{$fa-css-prefix}-thumbs-o-down:before { content: $fa-var-thumbs-o-down; } .#{$fa-css-prefix}-star-half:before { content: $fa-var-star-half; } .#{$fa-css-prefix}-heart-o:before { content: $fa-var-heart-o; } .#{$fa-css-prefix}-sign-out:before { content: $fa-var-sign-out; } .#{$fa-css-prefix}-linkedin-square:before { content: $fa-var-linkedin-square; } .#{$fa-css-prefix}-thumb-tack:before { content: $fa-var-thumb-tack; } .#{$fa-css-prefix}-external-link:before { content: $fa-var-external-link; } .#{$fa-css-prefix}-sign-in:before { content: $fa-var-sign-in; } .#{$fa-css-prefix}-trophy:before { content: $fa-var-trophy; } .#{$fa-css-prefix}-github-square:before { content: $fa-var-github-square; } .#{$fa-css-prefix}-upload:before { content: $fa-var-upload; } .#{$fa-css-prefix}-lemon-o:before { content: $fa-var-lemon-o; } .#{$fa-css-prefix}-phone:before { content: $fa-var-phone; } .#{$fa-css-prefix}-square-o:before { content: $fa-var-square-o; } .#{$fa-css-prefix}-bookmark-o:before { content: $fa-var-bookmark-o; } .#{$fa-css-prefix}-phone-square:before { content: $fa-var-phone-square; } .#{$fa-css-prefix}-twitter:before { content: $fa-var-twitter; } .#{$fa-css-prefix}-facebook-f:before, .#{$fa-css-prefix}-facebook:before { content: $fa-var-facebook; } .#{$fa-css-prefix}-github:before { content: $fa-var-github; } .#{$fa-css-prefix}-unlock:before { content: $fa-var-unlock; } .#{$fa-css-prefix}-credit-card:before { content: $fa-var-credit-card; } .#{$fa-css-prefix}-feed:before, .#{$fa-css-prefix}-rss:before { content: $fa-var-rss; } .#{$fa-css-prefix}-hdd-o:before { content: $fa-var-hdd-o; } .#{$fa-css-prefix}-bullhorn:before { content: $fa-var-bullhorn; } .#{$fa-css-prefix}-bell:before { content: $fa-var-bell; } .#{$fa-css-prefix}-certificate:before { content: $fa-var-certificate; } .#{$fa-css-prefix}-hand-o-right:before { content: $fa-var-hand-o-right; } .#{$fa-css-prefix}-hand-o-left:before { content: $fa-var-hand-o-left; } .#{$fa-css-prefix}-hand-o-up:before { content: $fa-var-hand-o-up; } .#{$fa-css-prefix}-hand-o-down:before { content: $fa-var-hand-o-down; } .#{$fa-css-prefix}-arrow-circle-left:before { content: $fa-var-arrow-circle-left; } .#{$fa-css-prefix}-arrow-circle-right:before { content: $fa-var-arrow-circle-right; } .#{$fa-css-prefix}-arrow-circle-up:before { content: $fa-var-arrow-circle-up; } .#{$fa-css-prefix}-arrow-circle-down:before { content: $fa-var-arrow-circle-down; } .#{$fa-css-prefix}-globe:before { content: $fa-var-globe; } .#{$fa-css-prefix}-wrench:before { content: $fa-var-wrench; } .#{$fa-css-prefix}-tasks:before { content: $fa-var-tasks; } .#{$fa-css-prefix}-filter:before { content: $fa-var-filter; } .#{$fa-css-prefix}-briefcase:before { content: $fa-var-briefcase; } .#{$fa-css-prefix}-arrows-alt:before { content: $fa-var-arrows-alt; } .#{$fa-css-prefix}-group:before, .#{$fa-css-prefix}-users:before { content: $fa-var-users; } .#{$fa-css-prefix}-chain:before, .#{$fa-css-prefix}-link:before { content: $fa-var-link; } .#{$fa-css-prefix}-cloud:before { content: $fa-var-cloud; } .#{$fa-css-prefix}-flask:before { content: $fa-var-flask; } .#{$fa-css-prefix}-cut:before, .#{$fa-css-prefix}-scissors:before { content: $fa-var-scissors; } .#{$fa-css-prefix}-copy:before, .#{$fa-css-prefix}-files-o:before { content: $fa-var-files-o; } .#{$fa-css-prefix}-paperclip:before { content: $fa-var-paperclip; } .#{$fa-css-prefix}-save:before, .#{$fa-css-prefix}-floppy-o:before { content: $fa-var-floppy-o; } .#{$fa-css-prefix}-square:before { content: $fa-var-square; } .#{$fa-css-prefix}-navicon:before, .#{$fa-css-prefix}-reorder:before, .#{$fa-css-prefix}-bars:before { content: $fa-var-bars; } .#{$fa-css-prefix}-list-ul:before { content: $fa-var-list-ul; } .#{$fa-css-prefix}-list-ol:before { content: $fa-var-list-ol; } .#{$fa-css-prefix}-strikethrough:before { content: $fa-var-strikethrough; } .#{$fa-css-prefix}-underline:before { content: $fa-var-underline; } .#{$fa-css-prefix}-table:before { content: $fa-var-table; } .#{$fa-css-prefix}-magic:before { content: $fa-var-magic; } .#{$fa-css-prefix}-truck:before { content: $fa-var-truck; } .#{$fa-css-prefix}-pinterest:before { content: $fa-var-pinterest; } .#{$fa-css-prefix}-pinterest-square:before { content: $fa-var-pinterest-square; } .#{$fa-css-prefix}-google-plus-square:before { content: $fa-var-google-plus-square; } .#{$fa-css-prefix}-google-plus:before { content: $fa-var-google-plus; } .#{$fa-css-prefix}-money:before { content: $fa-var-money; } .#{$fa-css-prefix}-caret-down:before { content: $fa-var-caret-down; } .#{$fa-css-prefix}-caret-up:before { content: $fa-var-caret-up; } .#{$fa-css-prefix}-caret-left:before { content: $fa-var-caret-left; } .#{$fa-css-prefix}-caret-right:before { content: $fa-var-caret-right; } .#{$fa-css-prefix}-columns:before { content: $fa-var-columns; } .#{$fa-css-prefix}-unsorted:before, .#{$fa-css-prefix}-sort:before { content: $fa-var-sort; } .#{$fa-css-prefix}-sort-down:before, .#{$fa-css-prefix}-sort-desc:before { content: $fa-var-sort-desc; } .#{$fa-css-prefix}-sort-up:before, .#{$fa-css-prefix}-sort-asc:before { content: $fa-var-sort-asc; } .#{$fa-css-prefix}-envelope:before { content: $fa-var-envelope; } .#{$fa-css-prefix}-linkedin:before { content: $fa-var-linkedin; } .#{$fa-css-prefix}-rotate-left:before, .#{$fa-css-prefix}-undo:before { content: $fa-var-undo; } .#{$fa-css-prefix}-legal:before, .#{$fa-css-prefix}-gavel:before { content: $fa-var-gavel; } .#{$fa-css-prefix}-dashboard:before, .#{$fa-css-prefix}-tachometer:before { content: $fa-var-tachometer; } .#{$fa-css-prefix}-comment-o:before { content: $fa-var-comment-o; } .#{$fa-css-prefix}-comments-o:before { content: $fa-var-comments-o; } .#{$fa-css-prefix}-flash:before, .#{$fa-css-prefix}-bolt:before { content: $fa-var-bolt; } .#{$fa-css-prefix}-sitemap:before { content: $fa-var-sitemap; } .#{$fa-css-prefix}-umbrella:before { content: $fa-var-umbrella; } .#{$fa-css-prefix}-paste:before, .#{$fa-css-prefix}-clipboard:before { content: $fa-var-clipboard; } .#{$fa-css-prefix}-lightbulb-o:before { content: $fa-var-lightbulb-o; } .#{$fa-css-prefix}-exchange:before { content: $fa-var-exchange; } .#{$fa-css-prefix}-cloud-download:before { content: $fa-var-cloud-download; } .#{$fa-css-prefix}-cloud-upload:before { content: $fa-var-cloud-upload; } .#{$fa-css-prefix}-user-md:before { content: $fa-var-user-md; } .#{$fa-css-prefix}-stethoscope:before { content: $fa-var-stethoscope; } .#{$fa-css-prefix}-suitcase:before { content: $fa-var-suitcase; } .#{$fa-css-prefix}-bell-o:before { content: $fa-var-bell-o; } .#{$fa-css-prefix}-coffee:before { content: $fa-var-coffee; } .#{$fa-css-prefix}-cutlery:before { content: $fa-var-cutlery; } .#{$fa-css-prefix}-file-text-o:before { content: $fa-var-file-text-o; } .#{$fa-css-prefix}-building-o:before { content: $fa-var-building-o; } .#{$fa-css-prefix}-hospital-o:before { content: $fa-var-hospital-o; } .#{$fa-css-prefix}-ambulance:before { content: $fa-var-ambulance; } .#{$fa-css-prefix}-medkit:before { content: $fa-var-medkit; } .#{$fa-css-prefix}-fighter-jet:before { content: $fa-var-fighter-jet; } .#{$fa-css-prefix}-beer:before { content: $fa-var-beer; } .#{$fa-css-prefix}-h-square:before { content: $fa-var-h-square; } .#{$fa-css-prefix}-plus-square:before { content: $fa-var-plus-square; } .#{$fa-css-prefix}-angle-double-left:before { content: $fa-var-angle-double-left; } .#{$fa-css-prefix}-angle-double-right:before { content: $fa-var-angle-double-right; } .#{$fa-css-prefix}-angle-double-up:before { content: $fa-var-angle-double-up; } .#{$fa-css-prefix}-angle-double-down:before { content: $fa-var-angle-double-down; } .#{$fa-css-prefix}-angle-left:before { content: $fa-var-angle-left; } .#{$fa-css-prefix}-angle-right:before { content: $fa-var-angle-right; } .#{$fa-css-prefix}-angle-up:before { content: $fa-var-angle-up; } .#{$fa-css-prefix}-angle-down:before { content: $fa-var-angle-down; } .#{$fa-css-prefix}-desktop:before { content: $fa-var-desktop; } .#{$fa-css-prefix}-laptop:before { content: $fa-var-laptop; } .#{$fa-css-prefix}-tablet:before { content: $fa-var-tablet; } .#{$fa-css-prefix}-mobile-phone:before, .#{$fa-css-prefix}-mobile:before { content: $fa-var-mobile; } .#{$fa-css-prefix}-circle-o:before { content: $fa-var-circle-o; } .#{$fa-css-prefix}-quote-left:before { content: $fa-var-quote-left; } .#{$fa-css-prefix}-quote-right:before { content: $fa-var-quote-right; } .#{$fa-css-prefix}-spinner:before { content: $fa-var-spinner; } .#{$fa-css-prefix}-circle:before { content: $fa-var-circle; } .#{$fa-css-prefix}-mail-reply:before, .#{$fa-css-prefix}-reply:before { content: $fa-var-reply; } .#{$fa-css-prefix}-github-alt:before { content: $fa-var-github-alt; } .#{$fa-css-prefix}-folder-o:before { content: $fa-var-folder-o; } .#{$fa-css-prefix}-folder-open-o:before { content: $fa-var-folder-open-o; } .#{$fa-css-prefix}-smile-o:before { content: $fa-var-smile-o; } .#{$fa-css-prefix}-frown-o:before { content: $fa-var-frown-o; } .#{$fa-css-prefix}-meh-o:before { content: $fa-var-meh-o; } .#{$fa-css-prefix}-gamepad:before { content: $fa-var-gamepad; } .#{$fa-css-prefix}-keyboard-o:before { content: $fa-var-keyboard-o; } .#{$fa-css-prefix}-flag-o:before { content: $fa-var-flag-o; } .#{$fa-css-prefix}-flag-checkered:before { content: $fa-var-flag-checkered; } .#{$fa-css-prefix}-terminal:before { content: $fa-var-terminal; } .#{$fa-css-prefix}-code:before { content: $fa-var-code; } .#{$fa-css-prefix}-mail-reply-all:before, .#{$fa-css-prefix}-reply-all:before { content: $fa-var-reply-all; } .#{$fa-css-prefix}-star-half-empty:before, .#{$fa-css-prefix}-star-half-full:before, .#{$fa-css-prefix}-star-half-o:before { content: $fa-var-star-half-o; } .#{$fa-css-prefix}-location-arrow:before { content: $fa-var-location-arrow; } .#{$fa-css-prefix}-crop:before { content: $fa-var-crop; } .#{$fa-css-prefix}-code-fork:before { content: $fa-var-code-fork; } .#{$fa-css-prefix}-unlink:before, .#{$fa-css-prefix}-chain-broken:before { content: $fa-var-chain-broken; } .#{$fa-css-prefix}-question:before { content: $fa-var-question; } .#{$fa-css-prefix}-info:before { content: $fa-var-info; } .#{$fa-css-prefix}-exclamation:before { content: $fa-var-exclamation; } .#{$fa-css-prefix}-superscript:before { content: $fa-var-superscript; } .#{$fa-css-prefix}-subscript:before { content: $fa-var-subscript; } .#{$fa-css-prefix}-eraser:before { content: $fa-var-eraser; } .#{$fa-css-prefix}-puzzle-piece:before { content: $fa-var-puzzle-piece; } .#{$fa-css-prefix}-microphone:before { content: $fa-var-microphone; } .#{$fa-css-prefix}-microphone-slash:before { content: $fa-var-microphone-slash; } .#{$fa-css-prefix}-shield:before { content: $fa-var-shield; } .#{$fa-css-prefix}-calendar-o:before { content: $fa-var-calendar-o; } .#{$fa-css-prefix}-fire-extinguisher:before { content: $fa-var-fire-extinguisher; } .#{$fa-css-prefix}-rocket:before { content: $fa-var-rocket; } .#{$fa-css-prefix}-maxcdn:before { content: $fa-var-maxcdn; } .#{$fa-css-prefix}-chevron-circle-left:before { content: $fa-var-chevron-circle-left; } .#{$fa-css-prefix}-chevron-circle-right:before { content: $fa-var-chevron-circle-right; } .#{$fa-css-prefix}-chevron-circle-up:before { content: $fa-var-chevron-circle-up; } .#{$fa-css-prefix}-chevron-circle-down:before { content: $fa-var-chevron-circle-down; } .#{$fa-css-prefix}-html5:before { content: $fa-var-html5; } .#{$fa-css-prefix}-css3:before { content: $fa-var-css3; } .#{$fa-css-prefix}-anchor:before { content: $fa-var-anchor; } .#{$fa-css-prefix}-unlock-alt:before { content: $fa-var-unlock-alt; } .#{$fa-css-prefix}-bullseye:before { content: $fa-var-bullseye; } .#{$fa-css-prefix}-ellipsis-h:before { content: $fa-var-ellipsis-h; } .#{$fa-css-prefix}-ellipsis-v:before { content: $fa-var-ellipsis-v; } .#{$fa-css-prefix}-rss-square:before { content: $fa-var-rss-square; } .#{$fa-css-prefix}-play-circle:before { content: $fa-var-play-circle; } .#{$fa-css-prefix}-ticket:before { content: $fa-var-ticket; } .#{$fa-css-prefix}-minus-square:before { content: $fa-var-minus-square; } .#{$fa-css-prefix}-minus-square-o:before { content: $fa-var-minus-square-o; } .#{$fa-css-prefix}-level-up:before { content: $fa-var-level-up; } .#{$fa-css-prefix}-level-down:before { content: $fa-var-level-down; } .#{$fa-css-prefix}-check-square:before { content: $fa-var-check-square; } .#{$fa-css-prefix}-pencil-square:before { content: $fa-var-pencil-square; } .#{$fa-css-prefix}-external-link-square:before { content: $fa-var-external-link-square; } .#{$fa-css-prefix}-share-square:before { content: $fa-var-share-square; } .#{$fa-css-prefix}-compass:before { content: $fa-var-compass; } .#{$fa-css-prefix}-toggle-down:before, .#{$fa-css-prefix}-caret-square-o-down:before { content: $fa-var-caret-square-o-down; } .#{$fa-css-prefix}-toggle-up:before, .#{$fa-css-prefix}-caret-square-o-up:before { content: $fa-var-caret-square-o-up; } .#{$fa-css-prefix}-toggle-right:before, .#{$fa-css-prefix}-caret-square-o-right:before { content: $fa-var-caret-square-o-right; } .#{$fa-css-prefix}-euro:before, .#{$fa-css-prefix}-eur:before { content: $fa-var-eur; } .#{$fa-css-prefix}-gbp:before { content: $fa-var-gbp; } .#{$fa-css-prefix}-dollar:before, .#{$fa-css-prefix}-usd:before { content: $fa-var-usd; } .#{$fa-css-prefix}-rupee:before, .#{$fa-css-prefix}-inr:before { content: $fa-var-inr; } .#{$fa-css-prefix}-cny:before, .#{$fa-css-prefix}-rmb:before, .#{$fa-css-prefix}-yen:before, .#{$fa-css-prefix}-jpy:before { content: $fa-var-jpy; } .#{$fa-css-prefix}-ruble:before, .#{$fa-css-prefix}-rouble:before, .#{$fa-css-prefix}-rub:before { content: $fa-var-rub; } .#{$fa-css-prefix}-won:before, .#{$fa-css-prefix}-krw:before { content: $fa-var-krw; } .#{$fa-css-prefix}-bitcoin:before, .#{$fa-css-prefix}-btc:before { content: $fa-var-btc; } .#{$fa-css-prefix}-file:before { content: $fa-var-file; } .#{$fa-css-prefix}-file-text:before { content: $fa-var-file-text; } .#{$fa-css-prefix}-sort-alpha-asc:before { content: $fa-var-sort-alpha-asc; } .#{$fa-css-prefix}-sort-alpha-desc:before { content: $fa-var-sort-alpha-desc; } .#{$fa-css-prefix}-sort-amount-asc:before { content: $fa-var-sort-amount-asc; } .#{$fa-css-prefix}-sort-amount-desc:before { content: $fa-var-sort-amount-desc; } .#{$fa-css-prefix}-sort-numeric-asc:before { content: $fa-var-sort-numeric-asc; } .#{$fa-css-prefix}-sort-numeric-desc:before { content: $fa-var-sort-numeric-desc; } .#{$fa-css-prefix}-thumbs-up:before { content: $fa-var-thumbs-up; } .#{$fa-css-prefix}-thumbs-down:before { content: $fa-var-thumbs-down; } .#{$fa-css-prefix}-youtube-square:before { content: $fa-var-youtube-square; } .#{$fa-css-prefix}-youtube:before { content: $fa-var-youtube; } .#{$fa-css-prefix}-xing:before { content: $fa-var-xing; } .#{$fa-css-prefix}-xing-square:before { content: $fa-var-xing-square; } .#{$fa-css-prefix}-youtube-play:before { content: $fa-var-youtube-play; } .#{$fa-css-prefix}-dropbox:before { content: $fa-var-dropbox; } .#{$fa-css-prefix}-stack-overflow:before { content: $fa-var-stack-overflow; } .#{$fa-css-prefix}-instagram:before { content: $fa-var-instagram; } .#{$fa-css-prefix}-flickr:before { content: $fa-var-flickr; } .#{$fa-css-prefix}-adn:before { content: $fa-var-adn; } .#{$fa-css-prefix}-bitbucket:before { content: $fa-var-bitbucket; } .#{$fa-css-prefix}-bitbucket-square:before { content: $fa-var-bitbucket-square; } .#{$fa-css-prefix}-tumblr:before { content: $fa-var-tumblr; } .#{$fa-css-prefix}-tumblr-square:before { content: $fa-var-tumblr-square; } .#{$fa-css-prefix}-long-arrow-down:before { content: $fa-var-long-arrow-down; } .#{$fa-css-prefix}-long-arrow-up:before { content: $fa-var-long-arrow-up; } .#{$fa-css-prefix}-long-arrow-left:before { content: $fa-var-long-arrow-left; } .#{$fa-css-prefix}-long-arrow-right:before { content: $fa-var-long-arrow-right; } .#{$fa-css-prefix}-apple:before { content: $fa-var-apple; } .#{$fa-css-prefix}-windows:before { content: $fa-var-windows; } .#{$fa-css-prefix}-android:before { content: $fa-var-android; } .#{$fa-css-prefix}-linux:before { content: $fa-var-linux; } .#{$fa-css-prefix}-dribbble:before { content: $fa-var-dribbble; } .#{$fa-css-prefix}-skype:before { content: $fa-var-skype; } .#{$fa-css-prefix}-foursquare:before { content: $fa-var-foursquare; } .#{$fa-css-prefix}-trello:before { content: $fa-var-trello; } .#{$fa-css-prefix}-female:before { content: $fa-var-female; } .#{$fa-css-prefix}-male:before { content: $fa-var-male; } .#{$fa-css-prefix}-gittip:before, .#{$fa-css-prefix}-gratipay:before { content: $fa-var-gratipay; } .#{$fa-css-prefix}-sun-o:before { content: $fa-var-sun-o; } .#{$fa-css-prefix}-moon-o:before { content: $fa-var-moon-o; } .#{$fa-css-prefix}-archive:before { content: $fa-var-archive; } .#{$fa-css-prefix}-bug:before { content: $fa-var-bug; } .#{$fa-css-prefix}-vk:before { content: $fa-var-vk; } .#{$fa-css-prefix}-weibo:before { content: $fa-var-weibo; } .#{$fa-css-prefix}-renren:before { content: $fa-var-renren; } .#{$fa-css-prefix}-pagelines:before { content: $fa-var-pagelines; } .#{$fa-css-prefix}-stack-exchange:before { content: $fa-var-stack-exchange; } .#{$fa-css-prefix}-arrow-circle-o-right:before { content: $fa-var-arrow-circle-o-right; } .#{$fa-css-prefix}-arrow-circle-o-left:before { content: $fa-var-arrow-circle-o-left; } .#{$fa-css-prefix}-toggle-left:before, .#{$fa-css-prefix}-caret-square-o-left:before { content: $fa-var-caret-square-o-left; } .#{$fa-css-prefix}-dot-circle-o:before { content: $fa-var-dot-circle-o; } .#{$fa-css-prefix}-wheelchair:before { content: $fa-var-wheelchair; } .#{$fa-css-prefix}-vimeo-square:before { content: $fa-var-vimeo-square; } .#{$fa-css-prefix}-turkish-lira:before, .#{$fa-css-prefix}-try:before { content: $fa-var-try; } .#{$fa-css-prefix}-plus-square-o:before { content: $fa-var-plus-square-o; } .#{$fa-css-prefix}-space-shuttle:before { content: $fa-var-space-shuttle; } .#{$fa-css-prefix}-slack:before { content: $fa-var-slack; } .#{$fa-css-prefix}-envelope-square:before { content: $fa-var-envelope-square; } .#{$fa-css-prefix}-wordpress:before { content: $fa-var-wordpress; } .#{$fa-css-prefix}-openid:before { content: $fa-var-openid; } .#{$fa-css-prefix}-institution:before, .#{$fa-css-prefix}-bank:before, .#{$fa-css-prefix}-university:before { content: $fa-var-university; } .#{$fa-css-prefix}-mortar-board:before, .#{$fa-css-prefix}-graduation-cap:before { content: $fa-var-graduation-cap; } .#{$fa-css-prefix}-yahoo:before { content: $fa-var-yahoo; } .#{$fa-css-prefix}-google:before { content: $fa-var-google; } .#{$fa-css-prefix}-reddit:before { content: $fa-var-reddit; } .#{$fa-css-prefix}-reddit-square:before { content: $fa-var-reddit-square; } .#{$fa-css-prefix}-stumbleupon-circle:before { content: $fa-var-stumbleupon-circle; } .#{$fa-css-prefix}-stumbleupon:before { content: $fa-var-stumbleupon; } .#{$fa-css-prefix}-delicious:before { content: $fa-var-delicious; } .#{$fa-css-prefix}-digg:before { content: $fa-var-digg; } .#{$fa-css-prefix}-pied-piper-pp:before { content: $fa-var-pied-piper-pp; } .#{$fa-css-prefix}-pied-piper-alt:before { content: $fa-var-pied-piper-alt; } .#{$fa-css-prefix}-drupal:before { content: $fa-var-drupal; } .#{$fa-css-prefix}-joomla:before { content: $fa-var-joomla; } .#{$fa-css-prefix}-language:before { content: $fa-var-language; } .#{$fa-css-prefix}-fax:before { content: $fa-var-fax; } .#{$fa-css-prefix}-building:before { content: $fa-var-building; } .#{$fa-css-prefix}-child:before { content: $fa-var-child; } .#{$fa-css-prefix}-paw:before { content: $fa-var-paw; } .#{$fa-css-prefix}-spoon:before { content: $fa-var-spoon; } .#{$fa-css-prefix}-cube:before { content: $fa-var-cube; } .#{$fa-css-prefix}-cubes:before { content: $fa-var-cubes; } .#{$fa-css-prefix}-behance:before { content: $fa-var-behance; } .#{$fa-css-prefix}-behance-square:before { content: $fa-var-behance-square; } .#{$fa-css-prefix}-steam:before { content: $fa-var-steam; } .#{$fa-css-prefix}-steam-square:before { content: $fa-var-steam-square; } .#{$fa-css-prefix}-recycle:before { content: $fa-var-recycle; } .#{$fa-css-prefix}-automobile:before, .#{$fa-css-prefix}-car:before { content: $fa-var-car; } .#{$fa-css-prefix}-cab:before, .#{$fa-css-prefix}-taxi:before { content: $fa-var-taxi; } .#{$fa-css-prefix}-tree:before { content: $fa-var-tree; } .#{$fa-css-prefix}-spotify:before { content: $fa-var-spotify; } .#{$fa-css-prefix}-deviantart:before { content: $fa-var-deviantart; } .#{$fa-css-prefix}-soundcloud:before { content: $fa-var-soundcloud; } .#{$fa-css-prefix}-database:before { content: $fa-var-database; } .#{$fa-css-prefix}-file-pdf-o:before { content: $fa-var-file-pdf-o; } .#{$fa-css-prefix}-file-word-o:before { content: $fa-var-file-word-o; } .#{$fa-css-prefix}-file-excel-o:before { content: $fa-var-file-excel-o; } .#{$fa-css-prefix}-file-powerpoint-o:before { content: $fa-var-file-powerpoint-o; } .#{$fa-css-prefix}-file-photo-o:before, .#{$fa-css-prefix}-file-picture-o:before, .#{$fa-css-prefix}-file-image-o:before { content: $fa-var-file-image-o; } .#{$fa-css-prefix}-file-zip-o:before, .#{$fa-css-prefix}-file-archive-o:before { content: $fa-var-file-archive-o; } .#{$fa-css-prefix}-file-sound-o:before, .#{$fa-css-prefix}-file-audio-o:before { content: $fa-var-file-audio-o; } .#{$fa-css-prefix}-file-movie-o:before, .#{$fa-css-prefix}-file-video-o:before { content: $fa-var-file-video-o; } .#{$fa-css-prefix}-file-code-o:before { content: $fa-var-file-code-o; } .#{$fa-css-prefix}-vine:before { content: $fa-var-vine; } .#{$fa-css-prefix}-codepen:before { content: $fa-var-codepen; } .#{$fa-css-prefix}-jsfiddle:before { content: $fa-var-jsfiddle; } .#{$fa-css-prefix}-life-bouy:before, .#{$fa-css-prefix}-life-buoy:before, .#{$fa-css-prefix}-life-saver:before, .#{$fa-css-prefix}-support:before, .#{$fa-css-prefix}-life-ring:before { content: $fa-var-life-ring; } .#{$fa-css-prefix}-circle-o-notch:before { content: $fa-var-circle-o-notch; } .#{$fa-css-prefix}-ra:before, .#{$fa-css-prefix}-resistance:before, .#{$fa-css-prefix}-rebel:before { content: $fa-var-rebel; } .#{$fa-css-prefix}-ge:before, .#{$fa-css-prefix}-empire:before { content: $fa-var-empire; } .#{$fa-css-prefix}-git-square:before { content: $fa-var-git-square; } .#{$fa-css-prefix}-git:before { content: $fa-var-git; } .#{$fa-css-prefix}-y-combinator-square:before, .#{$fa-css-prefix}-yc-square:before, .#{$fa-css-prefix}-hacker-news:before { content: $fa-var-hacker-news; } .#{$fa-css-prefix}-tencent-weibo:before { content: $fa-var-tencent-weibo; } .#{$fa-css-prefix}-qq:before { content: $fa-var-qq; } .#{$fa-css-prefix}-wechat:before, .#{$fa-css-prefix}-weixin:before { content: $fa-var-weixin; } .#{$fa-css-prefix}-send:before, .#{$fa-css-prefix}-paper-plane:before { content: $fa-var-paper-plane; } .#{$fa-css-prefix}-send-o:before, .#{$fa-css-prefix}-paper-plane-o:before { content: $fa-var-paper-plane-o; } .#{$fa-css-prefix}-history:before { content: $fa-var-history; } .#{$fa-css-prefix}-circle-thin:before { content: $fa-var-circle-thin; } .#{$fa-css-prefix}-header:before { content: $fa-var-header; } .#{$fa-css-prefix}-paragraph:before { content: $fa-var-paragraph; } .#{$fa-css-prefix}-sliders:before { content: $fa-var-sliders; } .#{$fa-css-prefix}-share-alt:before { content: $fa-var-share-alt; } .#{$fa-css-prefix}-share-alt-square:before { content: $fa-var-share-alt-square; } .#{$fa-css-prefix}-bomb:before { content: $fa-var-bomb; } .#{$fa-css-prefix}-soccer-ball-o:before, .#{$fa-css-prefix}-futbol-o:before { content: $fa-var-futbol-o; } .#{$fa-css-prefix}-tty:before { content: $fa-var-tty; } .#{$fa-css-prefix}-binoculars:before { content: $fa-var-binoculars; } .#{$fa-css-prefix}-plug:before { content: $fa-var-plug; } .#{$fa-css-prefix}-slideshare:before { content: $fa-var-slideshare; } .#{$fa-css-prefix}-twitch:before { content: $fa-var-twitch; } .#{$fa-css-prefix}-yelp:before { content: $fa-var-yelp; } .#{$fa-css-prefix}-newspaper-o:before { content: $fa-var-newspaper-o; } .#{$fa-css-prefix}-wifi:before { content: $fa-var-wifi; } .#{$fa-css-prefix}-calculator:before { content: $fa-var-calculator; } .#{$fa-css-prefix}-paypal:before { content: $fa-var-paypal; } .#{$fa-css-prefix}-google-wallet:before { content: $fa-var-google-wallet; } .#{$fa-css-prefix}-cc-visa:before { content: $fa-var-cc-visa; } .#{$fa-css-prefix}-cc-mastercard:before { content: $fa-var-cc-mastercard; } .#{$fa-css-prefix}-cc-discover:before { content: $fa-var-cc-discover; } .#{$fa-css-prefix}-cc-amex:before { content: $fa-var-cc-amex; } .#{$fa-css-prefix}-cc-paypal:before { content: $fa-var-cc-paypal; } .#{$fa-css-prefix}-cc-stripe:before { content: $fa-var-cc-stripe; } .#{$fa-css-prefix}-bell-slash:before { content: $fa-var-bell-slash; } .#{$fa-css-prefix}-bell-slash-o:before { content: $fa-var-bell-slash-o; } .#{$fa-css-prefix}-trash:before { content: $fa-var-trash; } .#{$fa-css-prefix}-copyright:before { content: $fa-var-copyright; } .#{$fa-css-prefix}-at:before { content: $fa-var-at; } .#{$fa-css-prefix}-eyedropper:before { content: $fa-var-eyedropper; } .#{$fa-css-prefix}-paint-brush:before { content: $fa-var-paint-brush; } .#{$fa-css-prefix}-birthday-cake:before { content: $fa-var-birthday-cake; } .#{$fa-css-prefix}-area-chart:before { content: $fa-var-area-chart; } .#{$fa-css-prefix}-pie-chart:before { content: $fa-var-pie-chart; } .#{$fa-css-prefix}-line-chart:before { content: $fa-var-line-chart; } .#{$fa-css-prefix}-lastfm:before { content: $fa-var-lastfm; } .#{$fa-css-prefix}-lastfm-square:before { content: $fa-var-lastfm-square; } .#{$fa-css-prefix}-toggle-off:before { content: $fa-var-toggle-off; } .#{$fa-css-prefix}-toggle-on:before { content: $fa-var-toggle-on; } .#{$fa-css-prefix}-bicycle:before { content: $fa-var-bicycle; } .#{$fa-css-prefix}-bus:before { content: $fa-var-bus; } .#{$fa-css-prefix}-ioxhost:before { content: $fa-var-ioxhost; } .#{$fa-css-prefix}-angellist:before { content: $fa-var-angellist; } .#{$fa-css-prefix}-cc:before { content: $fa-var-cc; } .#{$fa-css-prefix}-shekel:before, .#{$fa-css-prefix}-sheqel:before, .#{$fa-css-prefix}-ils:before { content: $fa-var-ils; } .#{$fa-css-prefix}-meanpath:before { content: $fa-var-meanpath; } .#{$fa-css-prefix}-buysellads:before { content: $fa-var-buysellads; } .#{$fa-css-prefix}-connectdevelop:before { content: $fa-var-connectdevelop; } .#{$fa-css-prefix}-dashcube:before { content: $fa-var-dashcube; } .#{$fa-css-prefix}-forumbee:before { content: $fa-var-forumbee; } .#{$fa-css-prefix}-leanpub:before { content: $fa-var-leanpub; } .#{$fa-css-prefix}-sellsy:before { content: $fa-var-sellsy; } .#{$fa-css-prefix}-shirtsinbulk:before { content: $fa-var-shirtsinbulk; } .#{$fa-css-prefix}-simplybuilt:before { content: $fa-var-simplybuilt; } .#{$fa-css-prefix}-skyatlas:before { content: $fa-var-skyatlas; } .#{$fa-css-prefix}-cart-plus:before { content: $fa-var-cart-plus; } .#{$fa-css-prefix}-cart-arrow-down:before { content: $fa-var-cart-arrow-down; } .#{$fa-css-prefix}-diamond:before { content: $fa-var-diamond; } .#{$fa-css-prefix}-ship:before { content: $fa-var-ship; } .#{$fa-css-prefix}-user-secret:before { content: $fa-var-user-secret; } .#{$fa-css-prefix}-motorcycle:before { content: $fa-var-motorcycle; } .#{$fa-css-prefix}-street-view:before { content: $fa-var-street-view; } .#{$fa-css-prefix}-heartbeat:before { content: $fa-var-heartbeat; } .#{$fa-css-prefix}-venus:before { content: $fa-var-venus; } .#{$fa-css-prefix}-mars:before { content: $fa-var-mars; } .#{$fa-css-prefix}-mercury:before { content: $fa-var-mercury; } .#{$fa-css-prefix}-intersex:before, .#{$fa-css-prefix}-transgender:before { content: $fa-var-transgender; } .#{$fa-css-prefix}-transgender-alt:before { content: $fa-var-transgender-alt; } .#{$fa-css-prefix}-venus-double:before { content: $fa-var-venus-double; } .#{$fa-css-prefix}-mars-double:before { content: $fa-var-mars-double; } .#{$fa-css-prefix}-venus-mars:before { content: $fa-var-venus-mars; } .#{$fa-css-prefix}-mars-stroke:before { content: $fa-var-mars-stroke; } .#{$fa-css-prefix}-mars-stroke-v:before { content: $fa-var-mars-stroke-v; } .#{$fa-css-prefix}-mars-stroke-h:before { content: $fa-var-mars-stroke-h; } .#{$fa-css-prefix}-neuter:before { content: $fa-var-neuter; } .#{$fa-css-prefix}-genderless:before { content: $fa-var-genderless; } .#{$fa-css-prefix}-facebook-official:before { content: $fa-var-facebook-official; } .#{$fa-css-prefix}-pinterest-p:before { content: $fa-var-pinterest-p; } .#{$fa-css-prefix}-whatsapp:before { content: $fa-var-whatsapp; } .#{$fa-css-prefix}-server:before { content: $fa-var-server; } .#{$fa-css-prefix}-user-plus:before { content: $fa-var-user-plus; } .#{$fa-css-prefix}-user-times:before { content: $fa-var-user-times; } .#{$fa-css-prefix}-hotel:before, .#{$fa-css-prefix}-bed:before { content: $fa-var-bed; } .#{$fa-css-prefix}-viacoin:before { content: $fa-var-viacoin; } .#{$fa-css-prefix}-train:before { content: $fa-var-train; } .#{$fa-css-prefix}-subway:before { content: $fa-var-subway; } .#{$fa-css-prefix}-medium:before { content: $fa-var-medium; } .#{$fa-css-prefix}-yc:before, .#{$fa-css-prefix}-y-combinator:before { content: $fa-var-y-combinator; } .#{$fa-css-prefix}-optin-monster:before { content: $fa-var-optin-monster; } .#{$fa-css-prefix}-opencart:before { content: $fa-var-opencart; } .#{$fa-css-prefix}-expeditedssl:before { content: $fa-var-expeditedssl; } .#{$fa-css-prefix}-battery-4:before, .#{$fa-css-prefix}-battery:before, .#{$fa-css-prefix}-battery-full:before { content: $fa-var-battery-full; } .#{$fa-css-prefix}-battery-3:before, .#{$fa-css-prefix}-battery-three-quarters:before { content: $fa-var-battery-three-quarters; } .#{$fa-css-prefix}-battery-2:before, .#{$fa-css-prefix}-battery-half:before { content: $fa-var-battery-half; } .#{$fa-css-prefix}-battery-1:before, .#{$fa-css-prefix}-battery-quarter:before { content: $fa-var-battery-quarter; } .#{$fa-css-prefix}-battery-0:before, .#{$fa-css-prefix}-battery-empty:before { content: $fa-var-battery-empty; } .#{$fa-css-prefix}-mouse-pointer:before { content: $fa-var-mouse-pointer; } .#{$fa-css-prefix}-i-cursor:before { content: $fa-var-i-cursor; } .#{$fa-css-prefix}-object-group:before { content: $fa-var-object-group; } .#{$fa-css-prefix}-object-ungroup:before { content: $fa-var-object-ungroup; } .#{$fa-css-prefix}-sticky-note:before { content: $fa-var-sticky-note; } .#{$fa-css-prefix}-sticky-note-o:before { content: $fa-var-sticky-note-o; } .#{$fa-css-prefix}-cc-jcb:before { content: $fa-var-cc-jcb; } .#{$fa-css-prefix}-cc-diners-club:before { content: $fa-var-cc-diners-club; } .#{$fa-css-prefix}-clone:before { content: $fa-var-clone; } .#{$fa-css-prefix}-balance-scale:before { content: $fa-var-balance-scale; } .#{$fa-css-prefix}-hourglass-o:before { content: $fa-var-hourglass-o; } .#{$fa-css-prefix}-hourglass-1:before, .#{$fa-css-prefix}-hourglass-start:before { content: $fa-var-hourglass-start; } .#{$fa-css-prefix}-hourglass-2:before, .#{$fa-css-prefix}-hourglass-half:before { content: $fa-var-hourglass-half; } .#{$fa-css-prefix}-hourglass-3:before, .#{$fa-css-prefix}-hourglass-end:before { content: $fa-var-hourglass-end; } .#{$fa-css-prefix}-hourglass:before { content: $fa-var-hourglass; } .#{$fa-css-prefix}-hand-grab-o:before, .#{$fa-css-prefix}-hand-rock-o:before { content: $fa-var-hand-rock-o; } .#{$fa-css-prefix}-hand-stop-o:before, .#{$fa-css-prefix}-hand-paper-o:before { content: $fa-var-hand-paper-o; } .#{$fa-css-prefix}-hand-scissors-o:before { content: $fa-var-hand-scissors-o; } .#{$fa-css-prefix}-hand-lizard-o:before { content: $fa-var-hand-lizard-o; } .#{$fa-css-prefix}-hand-spock-o:before { content: $fa-var-hand-spock-o; } .#{$fa-css-prefix}-hand-pointer-o:before { content: $fa-var-hand-pointer-o; } .#{$fa-css-prefix}-hand-peace-o:before { content: $fa-var-hand-peace-o; } .#{$fa-css-prefix}-trademark:before { content: $fa-var-trademark; } .#{$fa-css-prefix}-registered:before { content: $fa-var-registered; } .#{$fa-css-prefix}-creative-commons:before { content: $fa-var-creative-commons; } .#{$fa-css-prefix}-gg:before { content: $fa-var-gg; } .#{$fa-css-prefix}-gg-circle:before { content: $fa-var-gg-circle; } .#{$fa-css-prefix}-tripadvisor:before { content: $fa-var-tripadvisor; } .#{$fa-css-prefix}-odnoklassniki:before { content: $fa-var-odnoklassniki; } .#{$fa-css-prefix}-odnoklassniki-square:before { content: $fa-var-odnoklassniki-square; } .#{$fa-css-prefix}-get-pocket:before { content: $fa-var-get-pocket; } .#{$fa-css-prefix}-wikipedia-w:before { content: $fa-var-wikipedia-w; } .#{$fa-css-prefix}-safari:before { content: $fa-var-safari; } .#{$fa-css-prefix}-chrome:before { content: $fa-var-chrome; } .#{$fa-css-prefix}-firefox:before { content: $fa-var-firefox; } .#{$fa-css-prefix}-opera:before { content: $fa-var-opera; } .#{$fa-css-prefix}-internet-explorer:before { content: $fa-var-internet-explorer; } .#{$fa-css-prefix}-tv:before, .#{$fa-css-prefix}-television:before { content: $fa-var-television; } .#{$fa-css-prefix}-contao:before { content: $fa-var-contao; } .#{$fa-css-prefix}-500px:before { content: $fa-var-500px; } .#{$fa-css-prefix}-amazon:before { content: $fa-var-amazon; } .#{$fa-css-prefix}-calendar-plus-o:before { content: $fa-var-calendar-plus-o; } .#{$fa-css-prefix}-calendar-minus-o:before { content: $fa-var-calendar-minus-o; } .#{$fa-css-prefix}-calendar-times-o:before { content: $fa-var-calendar-times-o; } .#{$fa-css-prefix}-calendar-check-o:before { content: $fa-var-calendar-check-o; } .#{$fa-css-prefix}-industry:before { content: $fa-var-industry; } .#{$fa-css-prefix}-map-pin:before { content: $fa-var-map-pin; } .#{$fa-css-prefix}-map-signs:before { content: $fa-var-map-signs; } .#{$fa-css-prefix}-map-o:before { content: $fa-var-map-o; } .#{$fa-css-prefix}-map:before { content: $fa-var-map; } .#{$fa-css-prefix}-commenting:before { content: $fa-var-commenting; } .#{$fa-css-prefix}-commenting-o:before { content: $fa-var-commenting-o; } .#{$fa-css-prefix}-houzz:before { content: $fa-var-houzz; } .#{$fa-css-prefix}-vimeo:before { content: $fa-var-vimeo; } .#{$fa-css-prefix}-black-tie:before { content: $fa-var-black-tie; } .#{$fa-css-prefix}-fonticons:before { content: $fa-var-fonticons; } .#{$fa-css-prefix}-reddit-alien:before { content: $fa-var-reddit-alien; } .#{$fa-css-prefix}-edge:before { content: $fa-var-edge; } .#{$fa-css-prefix}-credit-card-alt:before { content: $fa-var-credit-card-alt; } .#{$fa-css-prefix}-codiepie:before { content: $fa-var-codiepie; } .#{$fa-css-prefix}-modx:before { content: $fa-var-modx; } .#{$fa-css-prefix}-fort-awesome:before { content: $fa-var-fort-awesome; } .#{$fa-css-prefix}-usb:before { content: $fa-var-usb; } .#{$fa-css-prefix}-product-hunt:before { content: $fa-var-product-hunt; } .#{$fa-css-prefix}-mixcloud:before { content: $fa-var-mixcloud; } .#{$fa-css-prefix}-scribd:before { content: $fa-var-scribd; } .#{$fa-css-prefix}-pause-circle:before { content: $fa-var-pause-circle; } .#{$fa-css-prefix}-pause-circle-o:before { content: $fa-var-pause-circle-o; } .#{$fa-css-prefix}-stop-circle:before { content: $fa-var-stop-circle; } .#{$fa-css-prefix}-stop-circle-o:before { content: $fa-var-stop-circle-o; } .#{$fa-css-prefix}-shopping-bag:before { content: $fa-var-shopping-bag; } .#{$fa-css-prefix}-shopping-basket:before { content: $fa-var-shopping-basket; } .#{$fa-css-prefix}-hashtag:before { content: $fa-var-hashtag; } .#{$fa-css-prefix}-bluetooth:before { content: $fa-var-bluetooth; } .#{$fa-css-prefix}-bluetooth-b:before { content: $fa-var-bluetooth-b; } .#{$fa-css-prefix}-percent:before { content: $fa-var-percent; } .#{$fa-css-prefix}-gitlab:before { content: $fa-var-gitlab; } .#{$fa-css-prefix}-wpbeginner:before { content: $fa-var-wpbeginner; } .#{$fa-css-prefix}-wpforms:before { content: $fa-var-wpforms; } .#{$fa-css-prefix}-envira:before { content: $fa-var-envira; } .#{$fa-css-prefix}-universal-access:before { content: $fa-var-universal-access; } .#{$fa-css-prefix}-wheelchair-alt:before { content: $fa-var-wheelchair-alt; } .#{$fa-css-prefix}-question-circle-o:before { content: $fa-var-question-circle-o; } .#{$fa-css-prefix}-blind:before { content: $fa-var-blind; } .#{$fa-css-prefix}-audio-description:before { content: $fa-var-audio-description; } .#{$fa-css-prefix}-volume-control-phone:before { content: $fa-var-volume-control-phone; } .#{$fa-css-prefix}-braille:before { content: $fa-var-braille; } .#{$fa-css-prefix}-assistive-listening-systems:before { content: $fa-var-assistive-listening-systems; } .#{$fa-css-prefix}-asl-interpreting:before, .#{$fa-css-prefix}-american-sign-language-interpreting:before { content: $fa-var-american-sign-language-interpreting; } .#{$fa-css-prefix}-deafness:before, .#{$fa-css-prefix}-hard-of-hearing:before, .#{$fa-css-prefix}-deaf:before { content: $fa-var-deaf; } .#{$fa-css-prefix}-glide:before { content: $fa-var-glide; } .#{$fa-css-prefix}-glide-g:before { content: $fa-var-glide-g; } .#{$fa-css-prefix}-signing:before, .#{$fa-css-prefix}-sign-language:before { content: $fa-var-sign-language; } .#{$fa-css-prefix}-low-vision:before { content: $fa-var-low-vision; } .#{$fa-css-prefix}-viadeo:before { content: $fa-var-viadeo; } .#{$fa-css-prefix}-viadeo-square:before { content: $fa-var-viadeo-square; } .#{$fa-css-prefix}-snapchat:before { content: $fa-var-snapchat; } .#{$fa-css-prefix}-snapchat-ghost:before { content: $fa-var-snapchat-ghost; } .#{$fa-css-prefix}-snapchat-square:before { content: $fa-var-snapchat-square; } .#{$fa-css-prefix}-pied-piper:before { content: $fa-var-pied-piper; } .#{$fa-css-prefix}-first-order:before { content: $fa-var-first-order; } .#{$fa-css-prefix}-yoast:before { content: $fa-var-yoast; } .#{$fa-css-prefix}-themeisle:before { content: $fa-var-themeisle; } .#{$fa-css-prefix}-google-plus-circle:before, .#{$fa-css-prefix}-google-plus-official:before { content: $fa-var-google-plus-official; } .#{$fa-css-prefix}-fa:before, .#{$fa-css-prefix}-font-awesome:before { content: $fa-var-font-awesome; } .#{$fa-css-prefix}-handshake-o:before { content: $fa-var-handshake-o; } .#{$fa-css-prefix}-envelope-open:before { content: $fa-var-envelope-open; } .#{$fa-css-prefix}-envelope-open-o:before { content: $fa-var-envelope-open-o; } .#{$fa-css-prefix}-linode:before { content: $fa-var-linode; } .#{$fa-css-prefix}-address-book:before { content: $fa-var-address-book; } .#{$fa-css-prefix}-address-book-o:before { content: $fa-var-address-book-o; } .#{$fa-css-prefix}-vcard:before, .#{$fa-css-prefix}-address-card:before { content: $fa-var-address-card; } .#{$fa-css-prefix}-vcard-o:before, .#{$fa-css-prefix}-address-card-o:before { content: $fa-var-address-card-o; } .#{$fa-css-prefix}-user-circle:before { content: $fa-var-user-circle; } .#{$fa-css-prefix}-user-circle-o:before { content: $fa-var-user-circle-o; } .#{$fa-css-prefix}-user-o:before { content: $fa-var-user-o; } .#{$fa-css-prefix}-id-badge:before { content: $fa-var-id-badge; } .#{$fa-css-prefix}-drivers-license:before, .#{$fa-css-prefix}-id-card:before { content: $fa-var-id-card; } .#{$fa-css-prefix}-drivers-license-o:before, .#{$fa-css-prefix}-id-card-o:before { content: $fa-var-id-card-o; } .#{$fa-css-prefix}-quora:before { content: $fa-var-quora; } .#{$fa-css-prefix}-free-code-camp:before { content: $fa-var-free-code-camp; } .#{$fa-css-prefix}-telegram:before { content: $fa-var-telegram; } .#{$fa-css-prefix}-thermometer-4:before, .#{$fa-css-prefix}-thermometer:before, .#{$fa-css-prefix}-thermometer-full:before { content: $fa-var-thermometer-full; } .#{$fa-css-prefix}-thermometer-3:before, .#{$fa-css-prefix}-thermometer-three-quarters:before { content: $fa-var-thermometer-three-quarters; } .#{$fa-css-prefix}-thermometer-2:before, .#{$fa-css-prefix}-thermometer-half:before { content: $fa-var-thermometer-half; } .#{$fa-css-prefix}-thermometer-1:before, .#{$fa-css-prefix}-thermometer-quarter:before { content: $fa-var-thermometer-quarter; } .#{$fa-css-prefix}-thermometer-0:before, .#{$fa-css-prefix}-thermometer-empty:before { content: $fa-var-thermometer-empty; } .#{$fa-css-prefix}-shower:before { content: $fa-var-shower; } .#{$fa-css-prefix}-bathtub:before, .#{$fa-css-prefix}-s15:before, .#{$fa-css-prefix}-bath:before { content: $fa-var-bath; } .#{$fa-css-prefix}-podcast:before { content: $fa-var-podcast; } .#{$fa-css-prefix}-window-maximize:before { content: $fa-var-window-maximize; } .#{$fa-css-prefix}-window-minimize:before { content: $fa-var-window-minimize; } .#{$fa-css-prefix}-window-restore:before { content: $fa-var-window-restore; } .#{$fa-css-prefix}-times-rectangle:before, .#{$fa-css-prefix}-window-close:before { content: $fa-var-window-close; } .#{$fa-css-prefix}-times-rectangle-o:before, .#{$fa-css-prefix}-window-close-o:before { content: $fa-var-window-close-o; } .#{$fa-css-prefix}-bandcamp:before { content: $fa-var-bandcamp; } .#{$fa-css-prefix}-grav:before { content: $fa-var-grav; } .#{$fa-css-prefix}-etsy:before { content: $fa-var-etsy; } .#{$fa-css-prefix}-imdb:before { content: $fa-var-imdb; } .#{$fa-css-prefix}-ravelry:before { content: $fa-var-ravelry; } .#{$fa-css-prefix}-eercast:before { content: $fa-var-eercast; } .#{$fa-css-prefix}-microchip:before { content: $fa-var-microchip; } .#{$fa-css-prefix}-snowflake-o:before { content: $fa-var-snowflake-o; } .#{$fa-css-prefix}-superpowers:before { content: $fa-var-superpowers; } .#{$fa-css-prefix}-wpexplorer:before { content: $fa-var-wpexplorer; } .#{$fa-css-prefix}-meetup:before { content: $fa-var-meetup; } ================================================ FILE: static/font-awesome-4.7.0/scss/_larger.scss ================================================ // Icon Sizes // ------------------------- /* makes the font 33% larger relative to the icon container */ .#{$fa-css-prefix}-lg { font-size: (4em / 3); line-height: (3em / 4); vertical-align: -15%; } .#{$fa-css-prefix}-2x { font-size: 2em; } .#{$fa-css-prefix}-3x { font-size: 3em; } .#{$fa-css-prefix}-4x { font-size: 4em; } .#{$fa-css-prefix}-5x { font-size: 5em; } ================================================ FILE: static/font-awesome-4.7.0/scss/_list.scss ================================================ // List Icons // ------------------------- .#{$fa-css-prefix}-ul { padding-left: 0; margin-left: $fa-li-width; list-style-type: none; > li { position: relative; } } .#{$fa-css-prefix}-li { position: absolute; left: -$fa-li-width; width: $fa-li-width; top: (2em / 14); text-align: center; &.#{$fa-css-prefix}-lg { left: -$fa-li-width + (4em / 14); } } ================================================ FILE: static/font-awesome-4.7.0/scss/_mixins.scss ================================================ // Mixins // -------------------------- @mixin fa-icon() { display: inline-block; font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration font-size: inherit; // can't have font-size inherit on line above, so need to override text-rendering: auto; // optimizelegibility throws things off #1094 -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } @mixin fa-icon-rotate($degrees, $rotation) { -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation})"; -webkit-transform: rotate($degrees); -ms-transform: rotate($degrees); transform: rotate($degrees); } @mixin fa-icon-flip($horiz, $vert, $rotation) { -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}, mirror=1)"; -webkit-transform: scale($horiz, $vert); -ms-transform: scale($horiz, $vert); transform: scale($horiz, $vert); } // Only display content to screen readers. A la Bootstrap 4. // // See: http://a11yproject.com/posts/how-to-hide-content/ @mixin sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); border: 0; } // Use in conjunction with .sr-only to only display content when it's focused. // // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 // // Credit: HTML5 Boilerplate @mixin sr-only-focusable { &:active, &:focus { position: static; width: auto; height: auto; margin: 0; overflow: visible; clip: auto; } } ================================================ FILE: static/font-awesome-4.7.0/scss/_path.scss ================================================ /* FONT PATH * -------------------------- */ @font-face { font-family: 'FontAwesome'; src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}'); src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'), url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') format('woff2'), url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'), url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'), url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg'); // src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts font-weight: normal; font-style: normal; } ================================================ FILE: static/font-awesome-4.7.0/scss/_rotated-flipped.scss ================================================ // Rotated & Flipped Icons // ------------------------- .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); } .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); } .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); } .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); } .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); } // Hook for IE8-9 // ------------------------- :root .#{$fa-css-prefix}-rotate-90, :root .#{$fa-css-prefix}-rotate-180, :root .#{$fa-css-prefix}-rotate-270, :root .#{$fa-css-prefix}-flip-horizontal, :root .#{$fa-css-prefix}-flip-vertical { filter: none; } ================================================ FILE: static/font-awesome-4.7.0/scss/_screen-reader.scss ================================================ // Screen Readers // ------------------------- .sr-only { @include sr-only(); } .sr-only-focusable { @include sr-only-focusable(); } ================================================ FILE: static/font-awesome-4.7.0/scss/_stacked.scss ================================================ // Stacked Icons // ------------------------- .#{$fa-css-prefix}-stack { position: relative; display: inline-block; width: 2em; height: 2em; line-height: 2em; vertical-align: middle; } .#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x { position: absolute; left: 0; width: 100%; text-align: center; } .#{$fa-css-prefix}-stack-1x { line-height: inherit; } .#{$fa-css-prefix}-stack-2x { font-size: 2em; } .#{$fa-css-prefix}-inverse { color: $fa-inverse; } ================================================ FILE: static/font-awesome-4.7.0/scss/_variables.scss ================================================ // Variables // -------------------------- $fa-font-path: "../fonts" !default; $fa-font-size-base: 14px !default; $fa-line-height-base: 1 !default; //$fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.7.0/fonts" !default; // for referencing Bootstrap CDN font files directly $fa-css-prefix: fa !default; $fa-version: "4.7.0" !default; $fa-border-color: #eee !default; $fa-inverse: #fff !default; $fa-li-width: (30em / 14) !default; $fa-var-500px: "\f26e"; $fa-var-address-book: "\f2b9"; $fa-var-address-book-o: "\f2ba"; $fa-var-address-card: "\f2bb"; $fa-var-address-card-o: "\f2bc"; $fa-var-adjust: "\f042"; $fa-var-adn: "\f170"; $fa-var-align-center: "\f037"; $fa-var-align-justify: "\f039"; $fa-var-align-left: "\f036"; $fa-var-align-right: "\f038"; $fa-var-amazon: "\f270"; $fa-var-ambulance: "\f0f9"; $fa-var-american-sign-language-interpreting: "\f2a3"; $fa-var-anchor: "\f13d"; $fa-var-android: "\f17b"; $fa-var-angellist: "\f209"; $fa-var-angle-double-down: "\f103"; $fa-var-angle-double-left: "\f100"; $fa-var-angle-double-right: "\f101"; $fa-var-angle-double-up: "\f102"; $fa-var-angle-down: "\f107"; $fa-var-angle-left: "\f104"; $fa-var-angle-right: "\f105"; $fa-var-angle-up: "\f106"; $fa-var-apple: "\f179"; $fa-var-archive: "\f187"; $fa-var-area-chart: "\f1fe"; $fa-var-arrow-circle-down: "\f0ab"; $fa-var-arrow-circle-left: "\f0a8"; $fa-var-arrow-circle-o-down: "\f01a"; $fa-var-arrow-circle-o-left: "\f190"; $fa-var-arrow-circle-o-right: "\f18e"; $fa-var-arrow-circle-o-up: "\f01b"; $fa-var-arrow-circle-right: "\f0a9"; $fa-var-arrow-circle-up: "\f0aa"; $fa-var-arrow-down: "\f063"; $fa-var-arrow-left: "\f060"; $fa-var-arrow-right: "\f061"; $fa-var-arrow-up: "\f062"; $fa-var-arrows: "\f047"; $fa-var-arrows-alt: "\f0b2"; $fa-var-arrows-h: "\f07e"; $fa-var-arrows-v: "\f07d"; $fa-var-asl-interpreting: "\f2a3"; $fa-var-assistive-listening-systems: "\f2a2"; $fa-var-asterisk: "\f069"; $fa-var-at: "\f1fa"; $fa-var-audio-description: "\f29e"; $fa-var-automobile: "\f1b9"; $fa-var-backward: "\f04a"; $fa-var-balance-scale: "\f24e"; $fa-var-ban: "\f05e"; $fa-var-bandcamp: "\f2d5"; $fa-var-bank: "\f19c"; $fa-var-bar-chart: "\f080"; $fa-var-bar-chart-o: "\f080"; $fa-var-barcode: "\f02a"; $fa-var-bars: "\f0c9"; $fa-var-bath: "\f2cd"; $fa-var-bathtub: "\f2cd"; $fa-var-battery: "\f240"; $fa-var-battery-0: "\f244"; $fa-var-battery-1: "\f243"; $fa-var-battery-2: "\f242"; $fa-var-battery-3: "\f241"; $fa-var-battery-4: "\f240"; $fa-var-battery-empty: "\f244"; $fa-var-battery-full: "\f240"; $fa-var-battery-half: "\f242"; $fa-var-battery-quarter: "\f243"; $fa-var-battery-three-quarters: "\f241"; $fa-var-bed: "\f236"; $fa-var-beer: "\f0fc"; $fa-var-behance: "\f1b4"; $fa-var-behance-square: "\f1b5"; $fa-var-bell: "\f0f3"; $fa-var-bell-o: "\f0a2"; $fa-var-bell-slash: "\f1f6"; $fa-var-bell-slash-o: "\f1f7"; $fa-var-bicycle: "\f206"; $fa-var-binoculars: "\f1e5"; $fa-var-birthday-cake: "\f1fd"; $fa-var-bitbucket: "\f171"; $fa-var-bitbucket-square: "\f172"; $fa-var-bitcoin: "\f15a"; $fa-var-black-tie: "\f27e"; $fa-var-blind: "\f29d"; $fa-var-bluetooth: "\f293"; $fa-var-bluetooth-b: "\f294"; $fa-var-bold: "\f032"; $fa-var-bolt: "\f0e7"; $fa-var-bomb: "\f1e2"; $fa-var-book: "\f02d"; $fa-var-bookmark: "\f02e"; $fa-var-bookmark-o: "\f097"; $fa-var-braille: "\f2a1"; $fa-var-briefcase: "\f0b1"; $fa-var-btc: "\f15a"; $fa-var-bug: "\f188"; $fa-var-building: "\f1ad"; $fa-var-building-o: "\f0f7"; $fa-var-bullhorn: "\f0a1"; $fa-var-bullseye: "\f140"; $fa-var-bus: "\f207"; $fa-var-buysellads: "\f20d"; $fa-var-cab: "\f1ba"; $fa-var-calculator: "\f1ec"; $fa-var-calendar: "\f073"; $fa-var-calendar-check-o: "\f274"; $fa-var-calendar-minus-o: "\f272"; $fa-var-calendar-o: "\f133"; $fa-var-calendar-plus-o: "\f271"; $fa-var-calendar-times-o: "\f273"; $fa-var-camera: "\f030"; $fa-var-camera-retro: "\f083"; $fa-var-car: "\f1b9"; $fa-var-caret-down: "\f0d7"; $fa-var-caret-left: "\f0d9"; $fa-var-caret-right: "\f0da"; $fa-var-caret-square-o-down: "\f150"; $fa-var-caret-square-o-left: "\f191"; $fa-var-caret-square-o-right: "\f152"; $fa-var-caret-square-o-up: "\f151"; $fa-var-caret-up: "\f0d8"; $fa-var-cart-arrow-down: "\f218"; $fa-var-cart-plus: "\f217"; $fa-var-cc: "\f20a"; $fa-var-cc-amex: "\f1f3"; $fa-var-cc-diners-club: "\f24c"; $fa-var-cc-discover: "\f1f2"; $fa-var-cc-jcb: "\f24b"; $fa-var-cc-mastercard: "\f1f1"; $fa-var-cc-paypal: "\f1f4"; $fa-var-cc-stripe: "\f1f5"; $fa-var-cc-visa: "\f1f0"; $fa-var-certificate: "\f0a3"; $fa-var-chain: "\f0c1"; $fa-var-chain-broken: "\f127"; $fa-var-check: "\f00c"; $fa-var-check-circle: "\f058"; $fa-var-check-circle-o: "\f05d"; $fa-var-check-square: "\f14a"; $fa-var-check-square-o: "\f046"; $fa-var-chevron-circle-down: "\f13a"; $fa-var-chevron-circle-left: "\f137"; $fa-var-chevron-circle-right: "\f138"; $fa-var-chevron-circle-up: "\f139"; $fa-var-chevron-down: "\f078"; $fa-var-chevron-left: "\f053"; $fa-var-chevron-right: "\f054"; $fa-var-chevron-up: "\f077"; $fa-var-child: "\f1ae"; $fa-var-chrome: "\f268"; $fa-var-circle: "\f111"; $fa-var-circle-o: "\f10c"; $fa-var-circle-o-notch: "\f1ce"; $fa-var-circle-thin: "\f1db"; $fa-var-clipboard: "\f0ea"; $fa-var-clock-o: "\f017"; $fa-var-clone: "\f24d"; $fa-var-close: "\f00d"; $fa-var-cloud: "\f0c2"; $fa-var-cloud-download: "\f0ed"; $fa-var-cloud-upload: "\f0ee"; $fa-var-cny: "\f157"; $fa-var-code: "\f121"; $fa-var-code-fork: "\f126"; $fa-var-codepen: "\f1cb"; $fa-var-codiepie: "\f284"; $fa-var-coffee: "\f0f4"; $fa-var-cog: "\f013"; $fa-var-cogs: "\f085"; $fa-var-columns: "\f0db"; $fa-var-comment: "\f075"; $fa-var-comment-o: "\f0e5"; $fa-var-commenting: "\f27a"; $fa-var-commenting-o: "\f27b"; $fa-var-comments: "\f086"; $fa-var-comments-o: "\f0e6"; $fa-var-compass: "\f14e"; $fa-var-compress: "\f066"; $fa-var-connectdevelop: "\f20e"; $fa-var-contao: "\f26d"; $fa-var-copy: "\f0c5"; $fa-var-copyright: "\f1f9"; $fa-var-creative-commons: "\f25e"; $fa-var-credit-card: "\f09d"; $fa-var-credit-card-alt: "\f283"; $fa-var-crop: "\f125"; $fa-var-crosshairs: "\f05b"; $fa-var-css3: "\f13c"; $fa-var-cube: "\f1b2"; $fa-var-cubes: "\f1b3"; $fa-var-cut: "\f0c4"; $fa-var-cutlery: "\f0f5"; $fa-var-dashboard: "\f0e4"; $fa-var-dashcube: "\f210"; $fa-var-database: "\f1c0"; $fa-var-deaf: "\f2a4"; $fa-var-deafness: "\f2a4"; $fa-var-dedent: "\f03b"; $fa-var-delicious: "\f1a5"; $fa-var-desktop: "\f108"; $fa-var-deviantart: "\f1bd"; $fa-var-diamond: "\f219"; $fa-var-digg: "\f1a6"; $fa-var-dollar: "\f155"; $fa-var-dot-circle-o: "\f192"; $fa-var-download: "\f019"; $fa-var-dribbble: "\f17d"; $fa-var-drivers-license: "\f2c2"; $fa-var-drivers-license-o: "\f2c3"; $fa-var-dropbox: "\f16b"; $fa-var-drupal: "\f1a9"; $fa-var-edge: "\f282"; $fa-var-edit: "\f044"; $fa-var-eercast: "\f2da"; $fa-var-eject: "\f052"; $fa-var-ellipsis-h: "\f141"; $fa-var-ellipsis-v: "\f142"; $fa-var-empire: "\f1d1"; $fa-var-envelope: "\f0e0"; $fa-var-envelope-o: "\f003"; $fa-var-envelope-open: "\f2b6"; $fa-var-envelope-open-o: "\f2b7"; $fa-var-envelope-square: "\f199"; $fa-var-envira: "\f299"; $fa-var-eraser: "\f12d"; $fa-var-etsy: "\f2d7"; $fa-var-eur: "\f153"; $fa-var-euro: "\f153"; $fa-var-exchange: "\f0ec"; $fa-var-exclamation: "\f12a"; $fa-var-exclamation-circle: "\f06a"; $fa-var-exclamation-triangle: "\f071"; $fa-var-expand: "\f065"; $fa-var-expeditedssl: "\f23e"; $fa-var-external-link: "\f08e"; $fa-var-external-link-square: "\f14c"; $fa-var-eye: "\f06e"; $fa-var-eye-slash: "\f070"; $fa-var-eyedropper: "\f1fb"; $fa-var-fa: "\f2b4"; $fa-var-facebook: "\f09a"; $fa-var-facebook-f: "\f09a"; $fa-var-facebook-official: "\f230"; $fa-var-facebook-square: "\f082"; $fa-var-fast-backward: "\f049"; $fa-var-fast-forward: "\f050"; $fa-var-fax: "\f1ac"; $fa-var-feed: "\f09e"; $fa-var-female: "\f182"; $fa-var-fighter-jet: "\f0fb"; $fa-var-file: "\f15b"; $fa-var-file-archive-o: "\f1c6"; $fa-var-file-audio-o: "\f1c7"; $fa-var-file-code-o: "\f1c9"; $fa-var-file-excel-o: "\f1c3"; $fa-var-file-image-o: "\f1c5"; $fa-var-file-movie-o: "\f1c8"; $fa-var-file-o: "\f016"; $fa-var-file-pdf-o: "\f1c1"; $fa-var-file-photo-o: "\f1c5"; $fa-var-file-picture-o: "\f1c5"; $fa-var-file-powerpoint-o: "\f1c4"; $fa-var-file-sound-o: "\f1c7"; $fa-var-file-text: "\f15c"; $fa-var-file-text-o: "\f0f6"; $fa-var-file-video-o: "\f1c8"; $fa-var-file-word-o: "\f1c2"; $fa-var-file-zip-o: "\f1c6"; $fa-var-files-o: "\f0c5"; $fa-var-film: "\f008"; $fa-var-filter: "\f0b0"; $fa-var-fire: "\f06d"; $fa-var-fire-extinguisher: "\f134"; $fa-var-firefox: "\f269"; $fa-var-first-order: "\f2b0"; $fa-var-flag: "\f024"; $fa-var-flag-checkered: "\f11e"; $fa-var-flag-o: "\f11d"; $fa-var-flash: "\f0e7"; $fa-var-flask: "\f0c3"; $fa-var-flickr: "\f16e"; $fa-var-floppy-o: "\f0c7"; $fa-var-folder: "\f07b"; $fa-var-folder-o: "\f114"; $fa-var-folder-open: "\f07c"; $fa-var-folder-open-o: "\f115"; $fa-var-font: "\f031"; $fa-var-font-awesome: "\f2b4"; $fa-var-fonticons: "\f280"; $fa-var-fort-awesome: "\f286"; $fa-var-forumbee: "\f211"; $fa-var-forward: "\f04e"; $fa-var-foursquare: "\f180"; $fa-var-free-code-camp: "\f2c5"; $fa-var-frown-o: "\f119"; $fa-var-futbol-o: "\f1e3"; $fa-var-gamepad: "\f11b"; $fa-var-gavel: "\f0e3"; $fa-var-gbp: "\f154"; $fa-var-ge: "\f1d1"; $fa-var-gear: "\f013"; $fa-var-gears: "\f085"; $fa-var-genderless: "\f22d"; $fa-var-get-pocket: "\f265"; $fa-var-gg: "\f260"; $fa-var-gg-circle: "\f261"; $fa-var-gift: "\f06b"; $fa-var-git: "\f1d3"; $fa-var-git-square: "\f1d2"; $fa-var-github: "\f09b"; $fa-var-github-alt: "\f113"; $fa-var-github-square: "\f092"; $fa-var-gitlab: "\f296"; $fa-var-gittip: "\f184"; $fa-var-glass: "\f000"; $fa-var-glide: "\f2a5"; $fa-var-glide-g: "\f2a6"; $fa-var-globe: "\f0ac"; $fa-var-google: "\f1a0"; $fa-var-google-plus: "\f0d5"; $fa-var-google-plus-circle: "\f2b3"; $fa-var-google-plus-official: "\f2b3"; $fa-var-google-plus-square: "\f0d4"; $fa-var-google-wallet: "\f1ee"; $fa-var-graduation-cap: "\f19d"; $fa-var-gratipay: "\f184"; $fa-var-grav: "\f2d6"; $fa-var-group: "\f0c0"; $fa-var-h-square: "\f0fd"; $fa-var-hacker-news: "\f1d4"; $fa-var-hand-grab-o: "\f255"; $fa-var-hand-lizard-o: "\f258"; $fa-var-hand-o-down: "\f0a7"; $fa-var-hand-o-left: "\f0a5"; $fa-var-hand-o-right: "\f0a4"; $fa-var-hand-o-up: "\f0a6"; $fa-var-hand-paper-o: "\f256"; $fa-var-hand-peace-o: "\f25b"; $fa-var-hand-pointer-o: "\f25a"; $fa-var-hand-rock-o: "\f255"; $fa-var-hand-scissors-o: "\f257"; $fa-var-hand-spock-o: "\f259"; $fa-var-hand-stop-o: "\f256"; $fa-var-handshake-o: "\f2b5"; $fa-var-hard-of-hearing: "\f2a4"; $fa-var-hashtag: "\f292"; $fa-var-hdd-o: "\f0a0"; $fa-var-header: "\f1dc"; $fa-var-headphones: "\f025"; $fa-var-heart: "\f004"; $fa-var-heart-o: "\f08a"; $fa-var-heartbeat: "\f21e"; $fa-var-history: "\f1da"; $fa-var-home: "\f015"; $fa-var-hospital-o: "\f0f8"; $fa-var-hotel: "\f236"; $fa-var-hourglass: "\f254"; $fa-var-hourglass-1: "\f251"; $fa-var-hourglass-2: "\f252"; $fa-var-hourglass-3: "\f253"; $fa-var-hourglass-end: "\f253"; $fa-var-hourglass-half: "\f252"; $fa-var-hourglass-o: "\f250"; $fa-var-hourglass-start: "\f251"; $fa-var-houzz: "\f27c"; $fa-var-html5: "\f13b"; $fa-var-i-cursor: "\f246"; $fa-var-id-badge: "\f2c1"; $fa-var-id-card: "\f2c2"; $fa-var-id-card-o: "\f2c3"; $fa-var-ils: "\f20b"; $fa-var-image: "\f03e"; $fa-var-imdb: "\f2d8"; $fa-var-inbox: "\f01c"; $fa-var-indent: "\f03c"; $fa-var-industry: "\f275"; $fa-var-info: "\f129"; $fa-var-info-circle: "\f05a"; $fa-var-inr: "\f156"; $fa-var-instagram: "\f16d"; $fa-var-institution: "\f19c"; $fa-var-internet-explorer: "\f26b"; $fa-var-intersex: "\f224"; $fa-var-ioxhost: "\f208"; $fa-var-italic: "\f033"; $fa-var-joomla: "\f1aa"; $fa-var-jpy: "\f157"; $fa-var-jsfiddle: "\f1cc"; $fa-var-key: "\f084"; $fa-var-keyboard-o: "\f11c"; $fa-var-krw: "\f159"; $fa-var-language: "\f1ab"; $fa-var-laptop: "\f109"; $fa-var-lastfm: "\f202"; $fa-var-lastfm-square: "\f203"; $fa-var-leaf: "\f06c"; $fa-var-leanpub: "\f212"; $fa-var-legal: "\f0e3"; $fa-var-lemon-o: "\f094"; $fa-var-level-down: "\f149"; $fa-var-level-up: "\f148"; $fa-var-life-bouy: "\f1cd"; $fa-var-life-buoy: "\f1cd"; $fa-var-life-ring: "\f1cd"; $fa-var-life-saver: "\f1cd"; $fa-var-lightbulb-o: "\f0eb"; $fa-var-line-chart: "\f201"; $fa-var-link: "\f0c1"; $fa-var-linkedin: "\f0e1"; $fa-var-linkedin-square: "\f08c"; $fa-var-linode: "\f2b8"; $fa-var-linux: "\f17c"; $fa-var-list: "\f03a"; $fa-var-list-alt: "\f022"; $fa-var-list-ol: "\f0cb"; $fa-var-list-ul: "\f0ca"; $fa-var-location-arrow: "\f124"; $fa-var-lock: "\f023"; $fa-var-long-arrow-down: "\f175"; $fa-var-long-arrow-left: "\f177"; $fa-var-long-arrow-right: "\f178"; $fa-var-long-arrow-up: "\f176"; $fa-var-low-vision: "\f2a8"; $fa-var-magic: "\f0d0"; $fa-var-magnet: "\f076"; $fa-var-mail-forward: "\f064"; $fa-var-mail-reply: "\f112"; $fa-var-mail-reply-all: "\f122"; $fa-var-male: "\f183"; $fa-var-map: "\f279"; $fa-var-map-marker: "\f041"; $fa-var-map-o: "\f278"; $fa-var-map-pin: "\f276"; $fa-var-map-signs: "\f277"; $fa-var-mars: "\f222"; $fa-var-mars-double: "\f227"; $fa-var-mars-stroke: "\f229"; $fa-var-mars-stroke-h: "\f22b"; $fa-var-mars-stroke-v: "\f22a"; $fa-var-maxcdn: "\f136"; $fa-var-meanpath: "\f20c"; $fa-var-medium: "\f23a"; $fa-var-medkit: "\f0fa"; $fa-var-meetup: "\f2e0"; $fa-var-meh-o: "\f11a"; $fa-var-mercury: "\f223"; $fa-var-microchip: "\f2db"; $fa-var-microphone: "\f130"; $fa-var-microphone-slash: "\f131"; $fa-var-minus: "\f068"; $fa-var-minus-circle: "\f056"; $fa-var-minus-square: "\f146"; $fa-var-minus-square-o: "\f147"; $fa-var-mixcloud: "\f289"; $fa-var-mobile: "\f10b"; $fa-var-mobile-phone: "\f10b"; $fa-var-modx: "\f285"; $fa-var-money: "\f0d6"; $fa-var-moon-o: "\f186"; $fa-var-mortar-board: "\f19d"; $fa-var-motorcycle: "\f21c"; $fa-var-mouse-pointer: "\f245"; $fa-var-music: "\f001"; $fa-var-navicon: "\f0c9"; $fa-var-neuter: "\f22c"; $fa-var-newspaper-o: "\f1ea"; $fa-var-object-group: "\f247"; $fa-var-object-ungroup: "\f248"; $fa-var-odnoklassniki: "\f263"; $fa-var-odnoklassniki-square: "\f264"; $fa-var-opencart: "\f23d"; $fa-var-openid: "\f19b"; $fa-var-opera: "\f26a"; $fa-var-optin-monster: "\f23c"; $fa-var-outdent: "\f03b"; $fa-var-pagelines: "\f18c"; $fa-var-paint-brush: "\f1fc"; $fa-var-paper-plane: "\f1d8"; $fa-var-paper-plane-o: "\f1d9"; $fa-var-paperclip: "\f0c6"; $fa-var-paragraph: "\f1dd"; $fa-var-paste: "\f0ea"; $fa-var-pause: "\f04c"; $fa-var-pause-circle: "\f28b"; $fa-var-pause-circle-o: "\f28c"; $fa-var-paw: "\f1b0"; $fa-var-paypal: "\f1ed"; $fa-var-pencil: "\f040"; $fa-var-pencil-square: "\f14b"; $fa-var-pencil-square-o: "\f044"; $fa-var-percent: "\f295"; $fa-var-phone: "\f095"; $fa-var-phone-square: "\f098"; $fa-var-photo: "\f03e"; $fa-var-picture-o: "\f03e"; $fa-var-pie-chart: "\f200"; $fa-var-pied-piper: "\f2ae"; $fa-var-pied-piper-alt: "\f1a8"; $fa-var-pied-piper-pp: "\f1a7"; $fa-var-pinterest: "\f0d2"; $fa-var-pinterest-p: "\f231"; $fa-var-pinterest-square: "\f0d3"; $fa-var-plane: "\f072"; $fa-var-play: "\f04b"; $fa-var-play-circle: "\f144"; $fa-var-play-circle-o: "\f01d"; $fa-var-plug: "\f1e6"; $fa-var-plus: "\f067"; $fa-var-plus-circle: "\f055"; $fa-var-plus-square: "\f0fe"; $fa-var-plus-square-o: "\f196"; $fa-var-podcast: "\f2ce"; $fa-var-power-off: "\f011"; $fa-var-print: "\f02f"; $fa-var-product-hunt: "\f288"; $fa-var-puzzle-piece: "\f12e"; $fa-var-qq: "\f1d6"; $fa-var-qrcode: "\f029"; $fa-var-question: "\f128"; $fa-var-question-circle: "\f059"; $fa-var-question-circle-o: "\f29c"; $fa-var-quora: "\f2c4"; $fa-var-quote-left: "\f10d"; $fa-var-quote-right: "\f10e"; $fa-var-ra: "\f1d0"; $fa-var-random: "\f074"; $fa-var-ravelry: "\f2d9"; $fa-var-rebel: "\f1d0"; $fa-var-recycle: "\f1b8"; $fa-var-reddit: "\f1a1"; $fa-var-reddit-alien: "\f281"; $fa-var-reddit-square: "\f1a2"; $fa-var-refresh: "\f021"; $fa-var-registered: "\f25d"; $fa-var-remove: "\f00d"; $fa-var-renren: "\f18b"; $fa-var-reorder: "\f0c9"; $fa-var-repeat: "\f01e"; $fa-var-reply: "\f112"; $fa-var-reply-all: "\f122"; $fa-var-resistance: "\f1d0"; $fa-var-retweet: "\f079"; $fa-var-rmb: "\f157"; $fa-var-road: "\f018"; $fa-var-rocket: "\f135"; $fa-var-rotate-left: "\f0e2"; $fa-var-rotate-right: "\f01e"; $fa-var-rouble: "\f158"; $fa-var-rss: "\f09e"; $fa-var-rss-square: "\f143"; $fa-var-rub: "\f158"; $fa-var-ruble: "\f158"; $fa-var-rupee: "\f156"; $fa-var-s15: "\f2cd"; $fa-var-safari: "\f267"; $fa-var-save: "\f0c7"; $fa-var-scissors: "\f0c4"; $fa-var-scribd: "\f28a"; $fa-var-search: "\f002"; $fa-var-search-minus: "\f010"; $fa-var-search-plus: "\f00e"; $fa-var-sellsy: "\f213"; $fa-var-send: "\f1d8"; $fa-var-send-o: "\f1d9"; $fa-var-server: "\f233"; $fa-var-share: "\f064"; $fa-var-share-alt: "\f1e0"; $fa-var-share-alt-square: "\f1e1"; $fa-var-share-square: "\f14d"; $fa-var-share-square-o: "\f045"; $fa-var-shekel: "\f20b"; $fa-var-sheqel: "\f20b"; $fa-var-shield: "\f132"; $fa-var-ship: "\f21a"; $fa-var-shirtsinbulk: "\f214"; $fa-var-shopping-bag: "\f290"; $fa-var-shopping-basket: "\f291"; $fa-var-shopping-cart: "\f07a"; $fa-var-shower: "\f2cc"; $fa-var-sign-in: "\f090"; $fa-var-sign-language: "\f2a7"; $fa-var-sign-out: "\f08b"; $fa-var-signal: "\f012"; $fa-var-signing: "\f2a7"; $fa-var-simplybuilt: "\f215"; $fa-var-sitemap: "\f0e8"; $fa-var-skyatlas: "\f216"; $fa-var-skype: "\f17e"; $fa-var-slack: "\f198"; $fa-var-sliders: "\f1de"; $fa-var-slideshare: "\f1e7"; $fa-var-smile-o: "\f118"; $fa-var-snapchat: "\f2ab"; $fa-var-snapchat-ghost: "\f2ac"; $fa-var-snapchat-square: "\f2ad"; $fa-var-snowflake-o: "\f2dc"; $fa-var-soccer-ball-o: "\f1e3"; $fa-var-sort: "\f0dc"; $fa-var-sort-alpha-asc: "\f15d"; $fa-var-sort-alpha-desc: "\f15e"; $fa-var-sort-amount-asc: "\f160"; $fa-var-sort-amount-desc: "\f161"; $fa-var-sort-asc: "\f0de"; $fa-var-sort-desc: "\f0dd"; $fa-var-sort-down: "\f0dd"; $fa-var-sort-numeric-asc: "\f162"; $fa-var-sort-numeric-desc: "\f163"; $fa-var-sort-up: "\f0de"; $fa-var-soundcloud: "\f1be"; $fa-var-space-shuttle: "\f197"; $fa-var-spinner: "\f110"; $fa-var-spoon: "\f1b1"; $fa-var-spotify: "\f1bc"; $fa-var-square: "\f0c8"; $fa-var-square-o: "\f096"; $fa-var-stack-exchange: "\f18d"; $fa-var-stack-overflow: "\f16c"; $fa-var-star: "\f005"; $fa-var-star-half: "\f089"; $fa-var-star-half-empty: "\f123"; $fa-var-star-half-full: "\f123"; $fa-var-star-half-o: "\f123"; $fa-var-star-o: "\f006"; $fa-var-steam: "\f1b6"; $fa-var-steam-square: "\f1b7"; $fa-var-step-backward: "\f048"; $fa-var-step-forward: "\f051"; $fa-var-stethoscope: "\f0f1"; $fa-var-sticky-note: "\f249"; $fa-var-sticky-note-o: "\f24a"; $fa-var-stop: "\f04d"; $fa-var-stop-circle: "\f28d"; $fa-var-stop-circle-o: "\f28e"; $fa-var-street-view: "\f21d"; $fa-var-strikethrough: "\f0cc"; $fa-var-stumbleupon: "\f1a4"; $fa-var-stumbleupon-circle: "\f1a3"; $fa-var-subscript: "\f12c"; $fa-var-subway: "\f239"; $fa-var-suitcase: "\f0f2"; $fa-var-sun-o: "\f185"; $fa-var-superpowers: "\f2dd"; $fa-var-superscript: "\f12b"; $fa-var-support: "\f1cd"; $fa-var-table: "\f0ce"; $fa-var-tablet: "\f10a"; $fa-var-tachometer: "\f0e4"; $fa-var-tag: "\f02b"; $fa-var-tags: "\f02c"; $fa-var-tasks: "\f0ae"; $fa-var-taxi: "\f1ba"; $fa-var-telegram: "\f2c6"; $fa-var-television: "\f26c"; $fa-var-tencent-weibo: "\f1d5"; $fa-var-terminal: "\f120"; $fa-var-text-height: "\f034"; $fa-var-text-width: "\f035"; $fa-var-th: "\f00a"; $fa-var-th-large: "\f009"; $fa-var-th-list: "\f00b"; $fa-var-themeisle: "\f2b2"; $fa-var-thermometer: "\f2c7"; $fa-var-thermometer-0: "\f2cb"; $fa-var-thermometer-1: "\f2ca"; $fa-var-thermometer-2: "\f2c9"; $fa-var-thermometer-3: "\f2c8"; $fa-var-thermometer-4: "\f2c7"; $fa-var-thermometer-empty: "\f2cb"; $fa-var-thermometer-full: "\f2c7"; $fa-var-thermometer-half: "\f2c9"; $fa-var-thermometer-quarter: "\f2ca"; $fa-var-thermometer-three-quarters: "\f2c8"; $fa-var-thumb-tack: "\f08d"; $fa-var-thumbs-down: "\f165"; $fa-var-thumbs-o-down: "\f088"; $fa-var-thumbs-o-up: "\f087"; $fa-var-thumbs-up: "\f164"; $fa-var-ticket: "\f145"; $fa-var-times: "\f00d"; $fa-var-times-circle: "\f057"; $fa-var-times-circle-o: "\f05c"; $fa-var-times-rectangle: "\f2d3"; $fa-var-times-rectangle-o: "\f2d4"; $fa-var-tint: "\f043"; $fa-var-toggle-down: "\f150"; $fa-var-toggle-left: "\f191"; $fa-var-toggle-off: "\f204"; $fa-var-toggle-on: "\f205"; $fa-var-toggle-right: "\f152"; $fa-var-toggle-up: "\f151"; $fa-var-trademark: "\f25c"; $fa-var-train: "\f238"; $fa-var-transgender: "\f224"; $fa-var-transgender-alt: "\f225"; $fa-var-trash: "\f1f8"; $fa-var-trash-o: "\f014"; $fa-var-tree: "\f1bb"; $fa-var-trello: "\f181"; $fa-var-tripadvisor: "\f262"; $fa-var-trophy: "\f091"; $fa-var-truck: "\f0d1"; $fa-var-try: "\f195"; $fa-var-tty: "\f1e4"; $fa-var-tumblr: "\f173"; $fa-var-tumblr-square: "\f174"; $fa-var-turkish-lira: "\f195"; $fa-var-tv: "\f26c"; $fa-var-twitch: "\f1e8"; $fa-var-twitter: "\f099"; $fa-var-twitter-square: "\f081"; $fa-var-umbrella: "\f0e9"; $fa-var-underline: "\f0cd"; $fa-var-undo: "\f0e2"; $fa-var-universal-access: "\f29a"; $fa-var-university: "\f19c"; $fa-var-unlink: "\f127"; $fa-var-unlock: "\f09c"; $fa-var-unlock-alt: "\f13e"; $fa-var-unsorted: "\f0dc"; $fa-var-upload: "\f093"; $fa-var-usb: "\f287"; $fa-var-usd: "\f155"; $fa-var-user: "\f007"; $fa-var-user-circle: "\f2bd"; $fa-var-user-circle-o: "\f2be"; $fa-var-user-md: "\f0f0"; $fa-var-user-o: "\f2c0"; $fa-var-user-plus: "\f234"; $fa-var-user-secret: "\f21b"; $fa-var-user-times: "\f235"; $fa-var-users: "\f0c0"; $fa-var-vcard: "\f2bb"; $fa-var-vcard-o: "\f2bc"; $fa-var-venus: "\f221"; $fa-var-venus-double: "\f226"; $fa-var-venus-mars: "\f228"; $fa-var-viacoin: "\f237"; $fa-var-viadeo: "\f2a9"; $fa-var-viadeo-square: "\f2aa"; $fa-var-video-camera: "\f03d"; $fa-var-vimeo: "\f27d"; $fa-var-vimeo-square: "\f194"; $fa-var-vine: "\f1ca"; $fa-var-vk: "\f189"; $fa-var-volume-control-phone: "\f2a0"; $fa-var-volume-down: "\f027"; $fa-var-volume-off: "\f026"; $fa-var-volume-up: "\f028"; $fa-var-warning: "\f071"; $fa-var-wechat: "\f1d7"; $fa-var-weibo: "\f18a"; $fa-var-weixin: "\f1d7"; $fa-var-whatsapp: "\f232"; $fa-var-wheelchair: "\f193"; $fa-var-wheelchair-alt: "\f29b"; $fa-var-wifi: "\f1eb"; $fa-var-wikipedia-w: "\f266"; $fa-var-window-close: "\f2d3"; $fa-var-window-close-o: "\f2d4"; $fa-var-window-maximize: "\f2d0"; $fa-var-window-minimize: "\f2d1"; $fa-var-window-restore: "\f2d2"; $fa-var-windows: "\f17a"; $fa-var-won: "\f159"; $fa-var-wordpress: "\f19a"; $fa-var-wpbeginner: "\f297"; $fa-var-wpexplorer: "\f2de"; $fa-var-wpforms: "\f298"; $fa-var-wrench: "\f0ad"; $fa-var-xing: "\f168"; $fa-var-xing-square: "\f169"; $fa-var-y-combinator: "\f23b"; $fa-var-y-combinator-square: "\f1d4"; $fa-var-yahoo: "\f19e"; $fa-var-yc: "\f23b"; $fa-var-yc-square: "\f1d4"; $fa-var-yelp: "\f1e9"; $fa-var-yen: "\f157"; $fa-var-yoast: "\f2b1"; $fa-var-youtube: "\f167"; $fa-var-youtube-play: "\f16a"; $fa-var-youtube-square: "\f166"; ================================================ FILE: static/font-awesome-4.7.0/scss/font-awesome.scss ================================================ /*! * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) */ @import "variables"; @import "mixins"; @import "path"; @import "core"; @import "larger"; @import "fixed-width"; @import "list"; @import "bordered-pulled"; @import "animated"; @import "rotated-flipped"; @import "stacked"; @import "icons"; @import "screen-reader"; ================================================ FILE: static/js/bootstrap.js ================================================ /*! * Bootstrap v3.3.7 (http://getbootstrap.com) * Copyright 2011-2016 Twitter, Inc. * Licensed under the MIT license */ if (typeof jQuery === 'undefined') { throw new Error('Bootstrap\'s JavaScript requires jQuery') } +function ($) { 'use strict'; var version = $.fn.jquery.split(' ')[0].split('.') if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] > 3)) { throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4') } }(jQuery); /* ======================================================================== * Bootstrap: transition.js v3.3.7 * http://getbootstrap.com/javascript/#transitions * ======================================================================== * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) // ============================================================ function transitionEnd() { var el = document.createElement('bootstrap') var transEndEventNames = { WebkitTransition : 'webkitTransitionEnd', MozTransition : 'transitionend', OTransition : 'oTransitionEnd otransitionend', transition : 'transitionend' } for (var name in transEndEventNames) { if (el.style[name] !== undefined) { return { end: transEndEventNames[name] } } } return false // explicit for ie8 ( ._.) } // http://blog.alexmaccaw.com/css-transitions $.fn.emulateTransitionEnd = function (duration) { var called = false var $el = this $(this).one('bsTransitionEnd', function () { called = true }) var callback = function () { if (!called) $($el).trigger($.support.transition.end) } setTimeout(callback, duration) return this } $(function () { $.support.transition = transitionEnd() if (!$.support.transition) return $.event.special.bsTransitionEnd = { bindType: $.support.transition.end, delegateType: $.support.transition.end, handle: function (e) { if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) } } }) }(jQuery); /* ======================================================================== * Bootstrap: alert.js v3.3.7 * http://getbootstrap.com/javascript/#alerts * ======================================================================== * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // ALERT CLASS DEFINITION // ====================== var dismiss = '[data-dismiss="alert"]' var Alert = function (el) { $(el).on('click', dismiss, this.close) } Alert.VERSION = '3.3.7' Alert.TRANSITION_DURATION = 150 Alert.prototype.close = function (e) { var $this = $(this) var selector = $this.attr('data-target') if (!selector) { selector = $this.attr('href') selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 } var $parent = $(selector === '#' ? [] : selector) if (e) e.preventDefault() if (!$parent.length) { $parent = $this.closest('.alert') } $parent.trigger(e = $.Event('close.bs.alert')) if (e.isDefaultPrevented()) return $parent.removeClass('in') function removeElement() { // detach from parent, fire event then clean up data $parent.detach().trigger('closed.bs.alert').remove() } $.support.transition && $parent.hasClass('fade') ? $parent .one('bsTransitionEnd', removeElement) .emulateTransitionEnd(Alert.TRANSITION_DURATION) : removeElement() } // ALERT PLUGIN DEFINITION // ======================= function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.alert') if (!data) $this.data('bs.alert', (data = new Alert(this))) if (typeof option == 'string') data[option].call($this) }) } var old = $.fn.alert $.fn.alert = Plugin $.fn.alert.Constructor = Alert // ALERT NO CONFLICT // ================= $.fn.alert.noConflict = function () { $.fn.alert = old return this } // ALERT DATA-API // ============== $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) }(jQuery); /* ======================================================================== * Bootstrap: button.js v3.3.7 * http://getbootstrap.com/javascript/#buttons * ======================================================================== * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // BUTTON PUBLIC CLASS DEFINITION // ============================== var Button = function (element, options) { this.$element = $(element) this.options = $.extend({}, Button.DEFAULTS, options) this.isLoading = false } Button.VERSION = '3.3.7' Button.DEFAULTS = { loadingText: 'loading...' } Button.prototype.setState = function (state) { var d = 'disabled' var $el = this.$element var val = $el.is('input') ? 'val' : 'html' var data = $el.data() state += 'Text' if (data.resetText == null) $el.data('resetText', $el[val]()) // push to event loop to allow forms to submit setTimeout($.proxy(function () { $el[val](data[state] == null ? this.options[state] : data[state]) if (state == 'loadingText') { this.isLoading = true $el.addClass(d).attr(d, d).prop(d, true) } else if (this.isLoading) { this.isLoading = false $el.removeClass(d).removeAttr(d).prop(d, false) } }, this), 0) } Button.prototype.toggle = function () { var changed = true var $parent = this.$element.closest('[data-toggle="buttons"]') if ($parent.length) { var $input = this.$element.find('input') if ($input.prop('type') == 'radio') { if ($input.prop('checked')) changed = false $parent.find('.active').removeClass('active') this.$element.addClass('active') } else if ($input.prop('type') == 'checkbox') { if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false this.$element.toggleClass('active') } $input.prop('checked', this.$element.hasClass('active')) if (changed) $input.trigger('change') } else { this.$element.attr('aria-pressed', !this.$element.hasClass('active')) this.$element.toggleClass('active') } } // BUTTON PLUGIN DEFINITION // ======================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.button') var options = typeof option == 'object' && option if (!data) $this.data('bs.button', (data = new Button(this, options))) if (option == 'toggle') data.toggle() else if (option) data.setState(option) }) } var old = $.fn.button $.fn.button = Plugin $.fn.button.Constructor = Button // BUTTON NO CONFLICT // ================== $.fn.button.noConflict = function () { $.fn.button = old return this } // BUTTON DATA-API // =============== $(document) .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { var $btn = $(e.target).closest('.btn') Plugin.call($btn, 'toggle') if (!($(e.target).is('input[type="radio"], input[type="checkbox"]'))) { // Prevent double click on radios, and the double selections (so cancellation) on checkboxes e.preventDefault() // The target component still receive the focus if ($btn.is('input,button')) $btn.trigger('focus') else $btn.find('input:visible,button:visible').first().trigger('focus') } }) .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type)) }) }(jQuery); /* ======================================================================== * Bootstrap: carousel.js v3.3.7 * http://getbootstrap.com/javascript/#carousel * ======================================================================== * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // CAROUSEL CLASS DEFINITION // ========================= var Carousel = function (element, options) { this.$element = $(element) this.$indicators = this.$element.find('.carousel-indicators') this.options = options this.paused = null this.sliding = null this.interval = null this.$active = null this.$items = null this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)) this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) } Carousel.VERSION = '3.3.7' Carousel.TRANSITION_DURATION = 600 Carousel.DEFAULTS = { interval: 5000, pause: 'hover', wrap: true, keyboard: true } Carousel.prototype.keydown = function (e) { if (/input|textarea/i.test(e.target.tagName)) return switch (e.which) { case 37: this.prev(); break case 39: this.next(); break default: return } e.preventDefault() } Carousel.prototype.cycle = function (e) { e || (this.paused = false) this.interval && clearInterval(this.interval) this.options.interval && !this.paused && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) return this } Carousel.prototype.getItemIndex = function (item) { this.$items = item.parent().children('.item') return this.$items.index(item || this.$active) } Carousel.prototype.getItemForDirection = function (direction, active) { var activeIndex = this.getItemIndex(active) var willWrap = (direction == 'prev' && activeIndex === 0) || (direction == 'next' && activeIndex == (this.$items.length - 1)) if (willWrap && !this.options.wrap) return active var delta = direction == 'prev' ? -1 : 1 var itemIndex = (activeIndex + delta) % this.$items.length return this.$items.eq(itemIndex) } Carousel.prototype.to = function (pos) { var that = this var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) if (pos > (this.$items.length - 1) || pos < 0) return if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" if (activeIndex == pos) return this.pause().cycle() return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos)) } Carousel.prototype.pause = function (e) { e || (this.paused = true) if (this.$element.find('.next, .prev').length && $.support.transition) { this.$element.trigger($.support.transition.end) this.cycle(true) } this.interval = clearInterval(this.interval) return this } Carousel.prototype.next = function () { if (this.sliding) return return this.slide('next') } Carousel.prototype.prev = function () { if (this.sliding) return return this.slide('prev') } Carousel.prototype.slide = function (type, next) { var $active = this.$element.find('.item.active') var $next = next || this.getItemForDirection(type, $active) var isCycling = this.interval var direction = type == 'next' ? 'left' : 'right' var that = this if ($next.hasClass('active')) return (this.sliding = false) var relatedTarget = $next[0] var slideEvent = $.Event('slide.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) this.$element.trigger(slideEvent) if (slideEvent.isDefaultPrevented()) return this.sliding = true isCycling && this.pause() if (this.$indicators.length) { this.$indicators.find('.active').removeClass('active') var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) $nextIndicator && $nextIndicator.addClass('active') } var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" if ($.support.transition && this.$element.hasClass('slide')) { $next.addClass(type) $next[0].offsetWidth // force reflow $active.addClass(direction) $next.addClass(direction) $active .one('bsTransitionEnd', function () { $next.removeClass([type, direction].join(' ')).addClass('active') $active.removeClass(['active', direction].join(' ')) that.sliding = false setTimeout(function () { that.$element.trigger(slidEvent) }, 0) }) .emulateTransitionEnd(Carousel.TRANSITION_DURATION) } else { $active.removeClass('active') $next.addClass('active') this.sliding = false this.$element.trigger(slidEvent) } isCycling && this.cycle() return this } // CAROUSEL PLUGIN DEFINITION // ========================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.carousel') var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) var action = typeof option == 'string' ? option : options.slide if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) if (typeof option == 'number') data.to(option) else if (action) data[action]() else if (options.interval) data.pause().cycle() }) } var old = $.fn.carousel $.fn.carousel = Plugin $.fn.carousel.Constructor = Carousel // CAROUSEL NO CONFLICT // ==================== $.fn.carousel.noConflict = function () { $.fn.carousel = old return this } // CAROUSEL DATA-API // ================= var clickHandler = function (e) { var href var $this = $(this) var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 if (!$target.hasClass('carousel')) return var options = $.extend({}, $target.data(), $this.data()) var slideIndex = $this.attr('data-slide-to') if (slideIndex) options.interval = false Plugin.call($target, options) if (slideIndex) { $target.data('bs.carousel').to(slideIndex) } e.preventDefault() } $(document) .on('click.bs.carousel.data-api', '[data-slide]', clickHandler) .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler) $(window).on('load', function () { $('[data-ride="carousel"]').each(function () { var $carousel = $(this) Plugin.call($carousel, $carousel.data()) }) }) }(jQuery); /* ======================================================================== * Bootstrap: collapse.js v3.3.7 * http://getbootstrap.com/javascript/#collapse * ======================================================================== * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ /* jshint latedef: false */ +function ($) { 'use strict'; // COLLAPSE PUBLIC CLASS DEFINITION // ================================ var Collapse = function (element, options) { this.$element = $(element) this.options = $.extend({}, Collapse.DEFAULTS, options) this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' + '[data-toggle="collapse"][data-target="#' + element.id + '"]') this.transitioning = null if (this.options.parent) { this.$parent = this.getParent() } else { this.addAriaAndCollapsedClass(this.$element, this.$trigger) } if (this.options.toggle) this.toggle() } Collapse.VERSION = '3.3.7' Collapse.TRANSITION_DURATION = 350 Collapse.DEFAULTS = { toggle: true } Collapse.prototype.dimension = function () { var hasWidth = this.$element.hasClass('width') return hasWidth ? 'width' : 'height' } Collapse.prototype.show = function () { if (this.transitioning || this.$element.hasClass('in')) return var activesData var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing') if (actives && actives.length) { activesData = actives.data('bs.collapse') if (activesData && activesData.transitioning) return } var startEvent = $.Event('show.bs.collapse') this.$element.trigger(startEvent) if (startEvent.isDefaultPrevented()) return if (actives && actives.length) { Plugin.call(actives, 'hide') activesData || actives.data('bs.collapse', null) } var dimension = this.dimension() this.$element .removeClass('collapse') .addClass('collapsing')[dimension](0) .attr('aria-expanded', true) this.$trigger .removeClass('collapsed') .attr('aria-expanded', true) this.transitioning = 1 var complete = function () { this.$element .removeClass('collapsing') .addClass('collapse in')[dimension]('') this.transitioning = 0 this.$element .trigger('shown.bs.collapse') } if (!$.support.transition) return complete.call(this) var scrollSize = $.camelCase(['scroll', dimension].join('-')) this.$element .one('bsTransitionEnd', $.proxy(complete, this)) .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) } Collapse.prototype.hide = function () { if (this.transitioning || !this.$element.hasClass('in')) return var startEvent = $.Event('hide.bs.collapse') this.$element.trigger(startEvent) if (startEvent.isDefaultPrevented()) return var dimension = this.dimension() this.$element[dimension](this.$element[dimension]())[0].offsetHeight this.$element .addClass('collapsing') .removeClass('collapse in') .attr('aria-expanded', false) this.$trigger .addClass('collapsed') .attr('aria-expanded', false) this.transitioning = 1 var complete = function () { this.transitioning = 0 this.$element .removeClass('collapsing') .addClass('collapse') .trigger('hidden.bs.collapse') } if (!$.support.transition) return complete.call(this) this.$element [dimension](0) .one('bsTransitionEnd', $.proxy(complete, this)) .emulateTransitionEnd(Collapse.TRANSITION_DURATION) } Collapse.prototype.toggle = function () { this[this.$element.hasClass('in') ? 'hide' : 'show']() } Collapse.prototype.getParent = function () { return $(this.options.parent) .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]') .each($.proxy(function (i, element) { var $element = $(element) this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element) }, this)) .end() } Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) { var isOpen = $element.hasClass('in') $element.attr('aria-expanded', isOpen) $trigger .toggleClass('collapsed', !isOpen) .attr('aria-expanded', isOpen) } function getTargetFromTrigger($trigger) { var href var target = $trigger.attr('data-target') || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 return $(target) } // COLLAPSE PLUGIN DEFINITION // ========================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.collapse') var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) if (typeof option == 'string') data[option]() }) } var old = $.fn.collapse $.fn.collapse = Plugin $.fn.collapse.Constructor = Collapse // COLLAPSE NO CONFLICT // ==================== $.fn.collapse.noConflict = function () { $.fn.collapse = old return this } // COLLAPSE DATA-API // ================= $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { var $this = $(this) if (!$this.attr('data-target')) e.preventDefault() var $target = getTargetFromTrigger($this) var data = $target.data('bs.collapse') var option = data ? 'toggle' : $this.data() Plugin.call($target, option) }) }(jQuery); /* ======================================================================== * Bootstrap: dropdown.js v3.3.7 * http://getbootstrap.com/javascript/#dropdowns * ======================================================================== * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // DROPDOWN CLASS DEFINITION // ========================= var backdrop = '.dropdown-backdrop' var toggle = '[data-toggle="dropdown"]' var Dropdown = function (element) { $(element).on('click.bs.dropdown', this.toggle) } Dropdown.VERSION = '3.3.7' function getParent($this) { var selector = $this.attr('data-target') if (!selector) { selector = $this.attr('href') selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 } var $parent = selector && $(selector) return $parent && $parent.length ? $parent : $this.parent() } function clearMenus(e) { if (e && e.which === 3) return $(backdrop).remove() $(toggle).each(function () { var $this = $(this) var $parent = getParent($this) var relatedTarget = { relatedTarget: this } if (!$parent.hasClass('open')) return if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget)) if (e.isDefaultPrevented()) return $this.attr('aria-expanded', 'false') $parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget)) }) } Dropdown.prototype.toggle = function (e) { var $this = $(this) if ($this.is('.disabled, :disabled')) return var $parent = getParent($this) var isActive = $parent.hasClass('open') clearMenus() if (!isActive) { if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { // if mobile we use a backdrop because click events don't delegate $(document.createElement('div')) .addClass('dropdown-backdrop') .insertAfter($(this)) .on('click', clearMenus) } var relatedTarget = { relatedTarget: this } $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget)) if (e.isDefaultPrevented()) return $this .trigger('focus') .attr('aria-expanded', 'true') $parent .toggleClass('open') .trigger($.Event('shown.bs.dropdown', relatedTarget)) } return false } Dropdown.prototype.keydown = function (e) { if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return var $this = $(this) e.preventDefault() e.stopPropagation() if ($this.is('.disabled, :disabled')) return var $parent = getParent($this) var isActive = $parent.hasClass('open') if (!isActive && e.which != 27 || isActive && e.which == 27) { if (e.which == 27) $parent.find(toggle).trigger('focus') return $this.trigger('click') } var desc = ' li:not(.disabled):visible a' var $items = $parent.find('.dropdown-menu' + desc) if (!$items.length) return var index = $items.index(e.target) if (e.which == 38 && index > 0) index-- // up if (e.which == 40 && index < $items.length - 1) index++ // down if (!~index) index = 0 $items.eq(index).trigger('focus') } // DROPDOWN PLUGIN DEFINITION // ========================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.dropdown') if (!data) $this.data('bs.dropdown', (data = new Dropdown(this))) if (typeof option == 'string') data[option].call($this) }) } var old = $.fn.dropdown $.fn.dropdown = Plugin $.fn.dropdown.Constructor = Dropdown // DROPDOWN NO CONFLICT // ==================== $.fn.dropdown.noConflict = function () { $.fn.dropdown = old return this } // APPLY TO STANDARD DROPDOWN ELEMENTS // =================================== $(document) .on('click.bs.dropdown.data-api', clearMenus) .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle) .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown) .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown) }(jQuery); /* ======================================================================== * Bootstrap: modal.js v3.3.7 * http://getbootstrap.com/javascript/#modals * ======================================================================== * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // MODAL CLASS DEFINITION // ====================== var Modal = function (element, options) { this.options = options this.$body = $(document.body) this.$element = $(element) this.$dialog = this.$element.find('.modal-dialog') this.$backdrop = null this.isShown = null this.originalBodyPad = null this.scrollbarWidth = 0 this.ignoreBackdropClick = false if (this.options.remote) { this.$element .find('.modal-content') .load(this.options.remote, $.proxy(function () { this.$element.trigger('loaded.bs.modal') }, this)) } } Modal.VERSION = '3.3.7' Modal.TRANSITION_DURATION = 300 Modal.BACKDROP_TRANSITION_DURATION = 150 Modal.DEFAULTS = { backdrop: true, keyboard: true, show: true } Modal.prototype.toggle = function (_relatedTarget) { return this.isShown ? this.hide() : this.show(_relatedTarget) } Modal.prototype.show = function (_relatedTarget) { var that = this var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget }) this.$element.trigger(e) if (this.isShown || e.isDefaultPrevented()) return this.isShown = true this.checkScrollbar() this.setScrollbar() this.$body.addClass('modal-open') this.escape() this.resize() this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this)) this.$dialog.on('mousedown.dismiss.bs.modal', function () { that.$element.one('mouseup.dismiss.bs.modal', function (e) { if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true }) }) this.backdrop(function () { var transition = $.support.transition && that.$element.hasClass('fade') if (!that.$element.parent().length) { that.$element.appendTo(that.$body) // don't move modals dom position } that.$element .show() .scrollTop(0) that.adjustDialog() if (transition) { that.$element[0].offsetWidth // force reflow } that.$element.addClass('in') that.enforceFocus() var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget }) transition ? that.$dialog // wait for modal to slide in .one('bsTransitionEnd', function () { that.$element.trigger('focus').trigger(e) }) .emulateTransitionEnd(Modal.TRANSITION_DURATION) : that.$element.trigger('focus').trigger(e) }) } Modal.prototype.hide = function (e) { if (e) e.preventDefault() e = $.Event('hide.bs.modal') this.$element.trigger(e) if (!this.isShown || e.isDefaultPrevented()) return this.isShown = false this.escape() this.resize() $(document).off('focusin.bs.modal') this.$element .removeClass('in') .off('click.dismiss.bs.modal') .off('mouseup.dismiss.bs.modal') this.$dialog.off('mousedown.dismiss.bs.modal') $.support.transition && this.$element.hasClass('fade') ? this.$element .one('bsTransitionEnd', $.proxy(this.hideModal, this)) .emulateTransitionEnd(Modal.TRANSITION_DURATION) : this.hideModal() } Modal.prototype.enforceFocus = function () { $(document) .off('focusin.bs.modal') // guard against infinite focus loop .on('focusin.bs.modal', $.proxy(function (e) { if (document !== e.target && this.$element[0] !== e.target && !this.$element.has(e.target).length) { this.$element.trigger('focus') } }, this)) } Modal.prototype.escape = function () { if (this.isShown && this.options.keyboard) { this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) { e.which == 27 && this.hide() }, this)) } else if (!this.isShown) { this.$element.off('keydown.dismiss.bs.modal') } } Modal.prototype.resize = function () { if (this.isShown) { $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this)) } else { $(window).off('resize.bs.modal') } } Modal.prototype.hideModal = function () { var that = this this.$element.hide() this.backdrop(function () { that.$body.removeClass('modal-open') that.resetAdjustments() that.resetScrollbar() that.$element.trigger('hidden.bs.modal') }) } Modal.prototype.removeBackdrop = function () { this.$backdrop && this.$backdrop.remove() this.$backdrop = null } Modal.prototype.backdrop = function (callback) { var that = this var animate = this.$element.hasClass('fade') ? 'fade' : '' if (this.isShown && this.options.backdrop) { var doAnimate = $.support.transition && animate this.$backdrop = $(document.createElement('div')) .addClass('modal-backdrop ' + animate) .appendTo(this.$body) this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) { if (this.ignoreBackdropClick) { this.ignoreBackdropClick = false return } if (e.target !== e.currentTarget) return this.options.backdrop == 'static' ? this.$element[0].focus() : this.hide() }, this)) if (doAnimate) this.$backdrop[0].offsetWidth // force reflow this.$backdrop.addClass('in') if (!callback) return doAnimate ? this.$backdrop .one('bsTransitionEnd', callback) .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : callback() } else if (!this.isShown && this.$backdrop) { this.$backdrop.removeClass('in') var callbackRemove = function () { that.removeBackdrop() callback && callback() } $.support.transition && this.$element.hasClass('fade') ? this.$backdrop .one('bsTransitionEnd', callbackRemove) .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : callbackRemove() } else if (callback) { callback() } } // these following methods are used to handle overflowing modals Modal.prototype.handleUpdate = function () { this.adjustDialog() } Modal.prototype.adjustDialog = function () { var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight this.$element.css({ paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '', paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : '' }) } Modal.prototype.resetAdjustments = function () { this.$element.css({ paddingLeft: '', paddingRight: '' }) } Modal.prototype.checkScrollbar = function () { var fullWindowWidth = window.innerWidth if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8 var documentElementRect = document.documentElement.getBoundingClientRect() fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left) } this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth this.scrollbarWidth = this.measureScrollbar() } Modal.prototype.setScrollbar = function () { var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10) this.originalBodyPad = document.body.style.paddingRight || '' if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth) } Modal.prototype.resetScrollbar = function () { this.$body.css('padding-right', this.originalBodyPad) } Modal.prototype.measureScrollbar = function () { // thx walsh var scrollDiv = document.createElement('div') scrollDiv.className = 'modal-scrollbar-measure' this.$body.append(scrollDiv) var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth this.$body[0].removeChild(scrollDiv) return scrollbarWidth } // MODAL PLUGIN DEFINITION // ======================= function Plugin(option, _relatedTarget) { return this.each(function () { var $this = $(this) var data = $this.data('bs.modal') var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option) if (!data) $this.data('bs.modal', (data = new Modal(this, options))) if (typeof option == 'string') data[option](_relatedTarget) else if (options.show) data.show(_relatedTarget) }) } var old = $.fn.modal $.fn.modal = Plugin $.fn.modal.Constructor = Modal // MODAL NO CONFLICT // ================= $.fn.modal.noConflict = function () { $.fn.modal = old return this } // MODAL DATA-API // ============== $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) { var $this = $(this) var href = $this.attr('href') var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7 var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data()) if ($this.is('a')) e.preventDefault() $target.one('show.bs.modal', function (showEvent) { if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown $target.one('hidden.bs.modal', function () { $this.is(':visible') && $this.trigger('focus') }) }) Plugin.call($target, option, this) }) }(jQuery); /* ======================================================================== * Bootstrap: tooltip.js v3.3.7 * http://getbootstrap.com/javascript/#tooltip * Inspired by the original jQuery.tipsy by Jason Frame * ======================================================================== * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // TOOLTIP PUBLIC CLASS DEFINITION // =============================== var Tooltip = function (element, options) { this.type = null this.options = null this.enabled = null this.timeout = null this.hoverState = null this.$element = null this.inState = null this.init('tooltip', element, options) } Tooltip.VERSION = '3.3.7' Tooltip.TRANSITION_DURATION = 150 Tooltip.DEFAULTS = { animation: true, placement: 'top', selector: false, template: '', trigger: 'hover focus', title: '', delay: 0, html: false, container: false, viewport: { selector: 'body', padding: 0 } } Tooltip.prototype.init = function (type, element, options) { this.enabled = true this.type = type this.$element = $(element) this.options = this.getOptions(options) this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport)) this.inState = { click: false, hover: false, focus: false } if (this.$element[0] instanceof document.constructor && !this.options.selector) { throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!') } var triggers = this.options.trigger.split(' ') for (var i = triggers.length; i--;) { var trigger = triggers[i] if (trigger == 'click') { this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) } else if (trigger != 'manual') { var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin' var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout' this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) } } this.options.selector ? (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : this.fixTitle() } Tooltip.prototype.getDefaults = function () { return Tooltip.DEFAULTS } Tooltip.prototype.getOptions = function (options) { options = $.extend({}, this.getDefaults(), this.$element.data(), options) if (options.delay && typeof options.delay == 'number') { options.delay = { show: options.delay, hide: options.delay } } return options } Tooltip.prototype.getDelegateOptions = function () { var options = {} var defaults = this.getDefaults() this._options && $.each(this._options, function (key, value) { if (defaults[key] != value) options[key] = value }) return options } Tooltip.prototype.enter = function (obj) { var self = obj instanceof this.constructor ? obj : $(obj.currentTarget).data('bs.' + this.type) if (!self) { self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) $(obj.currentTarget).data('bs.' + this.type, self) } if (obj instanceof $.Event) { self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true } if (self.tip().hasClass('in') || self.hoverState == 'in') { self.hoverState = 'in' return } clearTimeout(self.timeout) self.hoverState = 'in' if (!self.options.delay || !self.options.delay.show) return self.show() self.timeout = setTimeout(function () { if (self.hoverState == 'in') self.show() }, self.options.delay.show) } Tooltip.prototype.isInStateTrue = function () { for (var key in this.inState) { if (this.inState[key]) return true } return false } Tooltip.prototype.leave = function (obj) { var self = obj instanceof this.constructor ? obj : $(obj.currentTarget).data('bs.' + this.type) if (!self) { self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) $(obj.currentTarget).data('bs.' + this.type, self) } if (obj instanceof $.Event) { self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false } if (self.isInStateTrue()) return clearTimeout(self.timeout) self.hoverState = 'out' if (!self.options.delay || !self.options.delay.hide) return self.hide() self.timeout = setTimeout(function () { if (self.hoverState == 'out') self.hide() }, self.options.delay.hide) } Tooltip.prototype.show = function () { var e = $.Event('show.bs.' + this.type) if (this.hasContent() && this.enabled) { this.$element.trigger(e) var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0]) if (e.isDefaultPrevented() || !inDom) return var that = this var $tip = this.tip() var tipId = this.getUID(this.type) this.setContent() $tip.attr('id', tipId) this.$element.attr('aria-describedby', tipId) if (this.options.animation) $tip.addClass('fade') var placement = typeof this.options.placement == 'function' ? this.options.placement.call(this, $tip[0], this.$element[0]) : this.options.placement var autoToken = /\s?auto?\s?/i var autoPlace = autoToken.test(placement) if (autoPlace) placement = placement.replace(autoToken, '') || 'top' $tip .detach() .css({ top: 0, left: 0, display: 'block' }) .addClass(placement) .data('bs.' + this.type, this) this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element) this.$element.trigger('inserted.bs.' + this.type) var pos = this.getPosition() var actualWidth = $tip[0].offsetWidth var actualHeight = $tip[0].offsetHeight if (autoPlace) { var orgPlacement = placement var viewportDim = this.getPosition(this.$viewport) placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top' : placement == 'top' && pos.top - actualHeight < viewportDim.top ? 'bottom' : placement == 'right' && pos.right + actualWidth > viewportDim.width ? 'left' : placement == 'left' && pos.left - actualWidth < viewportDim.left ? 'right' : placement $tip .removeClass(orgPlacement) .addClass(placement) } var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight) this.applyPlacement(calculatedOffset, placement) var complete = function () { var prevHoverState = that.hoverState that.$element.trigger('shown.bs.' + that.type) that.hoverState = null if (prevHoverState == 'out') that.leave(that) } $.support.transition && this.$tip.hasClass('fade') ? $tip .one('bsTransitionEnd', complete) .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : complete() } } Tooltip.prototype.applyPlacement = function (offset, placement) { var $tip = this.tip() var width = $tip[0].offsetWidth var height = $tip[0].offsetHeight // manually read margins because getBoundingClientRect includes difference var marginTop = parseInt($tip.css('margin-top'), 10) var marginLeft = parseInt($tip.css('margin-left'), 10) // we must check for NaN for ie 8/9 if (isNaN(marginTop)) marginTop = 0 if (isNaN(marginLeft)) marginLeft = 0 offset.top += marginTop offset.left += marginLeft // $.fn.offset doesn't round pixel values // so we use setOffset directly with our own function B-0 $.offset.setOffset($tip[0], $.extend({ using: function (props) { $tip.css({ top: Math.round(props.top), left: Math.round(props.left) }) } }, offset), 0) $tip.addClass('in') // check to see if placing tip in new offset caused the tip to resize itself var actualWidth = $tip[0].offsetWidth var actualHeight = $tip[0].offsetHeight if (placement == 'top' && actualHeight != height) { offset.top = offset.top + height - actualHeight } var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight) if (delta.left) offset.left += delta.left else offset.top += delta.top var isVertical = /top|bottom/.test(placement) var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight' $tip.offset(offset) this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical) } Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) { this.arrow() .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%') .css(isVertical ? 'top' : 'left', '') } Tooltip.prototype.setContent = function () { var $tip = this.tip() var title = this.getTitle() $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) $tip.removeClass('fade in top bottom left right') } Tooltip.prototype.hide = function (callback) { var that = this var $tip = $(this.$tip) var e = $.Event('hide.bs.' + this.type) function complete() { if (that.hoverState != 'in') $tip.detach() if (that.$element) { // TODO: Check whether guarding this code with this `if` is really necessary. that.$element .removeAttr('aria-describedby') .trigger('hidden.bs.' + that.type) } callback && callback() } this.$element.trigger(e) if (e.isDefaultPrevented()) return $tip.removeClass('in') $.support.transition && $tip.hasClass('fade') ? $tip .one('bsTransitionEnd', complete) .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : complete() this.hoverState = null return this } Tooltip.prototype.fixTitle = function () { var $e = this.$element if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') { $e.attr('data-original-title', $e.attr('title') || '').attr('title', '') } } Tooltip.prototype.hasContent = function () { return this.getTitle() } Tooltip.prototype.getPosition = function ($element) { $element = $element || this.$element var el = $element[0] var isBody = el.tagName == 'BODY' var elRect = el.getBoundingClientRect() if (elRect.width == null) { // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093 elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top }) } var isSvg = window.SVGElement && el instanceof window.SVGElement // Avoid using $.offset() on SVGs since it gives incorrect results in jQuery 3. // See https://github.com/twbs/bootstrap/issues/20280 var elOffset = isBody ? { top: 0, left: 0 } : (isSvg ? null : $element.offset()) var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() } var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null return $.extend({}, elRect, scroll, outerDims, elOffset) } Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) { return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } : placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } : placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } : /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width } } Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) { var delta = { top: 0, left: 0 } if (!this.$viewport) return delta var viewportPadding = this.options.viewport && this.options.viewport.padding || 0 var viewportDimensions = this.getPosition(this.$viewport) if (/right|left/.test(placement)) { var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight if (topEdgeOffset < viewportDimensions.top) { // top overflow delta.top = viewportDimensions.top - topEdgeOffset } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset } } else { var leftEdgeOffset = pos.left - viewportPadding var rightEdgeOffset = pos.left + viewportPadding + actualWidth if (leftEdgeOffset < viewportDimensions.left) { // left overflow delta.left = viewportDimensions.left - leftEdgeOffset } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset } } return delta } Tooltip.prototype.getTitle = function () { var title var $e = this.$element var o = this.options title = $e.attr('data-original-title') || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) return title } Tooltip.prototype.getUID = function (prefix) { do prefix += ~~(Math.random() * 1000000) while (document.getElementById(prefix)) return prefix } Tooltip.prototype.tip = function () { if (!this.$tip) { this.$tip = $(this.options.template) if (this.$tip.length != 1) { throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!') } } return this.$tip } Tooltip.prototype.arrow = function () { return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')) } Tooltip.prototype.enable = function () { this.enabled = true } Tooltip.prototype.disable = function () { this.enabled = false } Tooltip.prototype.toggleEnabled = function () { this.enabled = !this.enabled } Tooltip.prototype.toggle = function (e) { var self = this if (e) { self = $(e.currentTarget).data('bs.' + this.type) if (!self) { self = new this.constructor(e.currentTarget, this.getDelegateOptions()) $(e.currentTarget).data('bs.' + this.type, self) } } if (e) { self.inState.click = !self.inState.click if (self.isInStateTrue()) self.enter(self) else self.leave(self) } else { self.tip().hasClass('in') ? self.leave(self) : self.enter(self) } } Tooltip.prototype.destroy = function () { var that = this clearTimeout(this.timeout) this.hide(function () { that.$element.off('.' + that.type).removeData('bs.' + that.type) if (that.$tip) { that.$tip.detach() } that.$tip = null that.$arrow = null that.$viewport = null that.$element = null }) } // TOOLTIP PLUGIN DEFINITION // ========================= function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.tooltip') var options = typeof option == 'object' && option if (!data && /destroy|hide/.test(option)) return if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))) if (typeof option == 'string') data[option]() }) } var old = $.fn.tooltip $.fn.tooltip = Plugin $.fn.tooltip.Constructor = Tooltip // TOOLTIP NO CONFLICT // =================== $.fn.tooltip.noConflict = function () { $.fn.tooltip = old return this } }(jQuery); /* ======================================================================== * Bootstrap: popover.js v3.3.7 * http://getbootstrap.com/javascript/#popovers * ======================================================================== * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // POPOVER PUBLIC CLASS DEFINITION // =============================== var Popover = function (element, options) { this.init('popover', element, options) } if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js') Popover.VERSION = '3.3.7' Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, { placement: 'right', trigger: 'click', content: '', template: '' }) // NOTE: POPOVER EXTENDS tooltip.js // ================================ Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype) Popover.prototype.constructor = Popover Popover.prototype.getDefaults = function () { return Popover.DEFAULTS } Popover.prototype.setContent = function () { var $tip = this.tip() var title = this.getTitle() var content = this.getContent() $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title) $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text' ](content) $tip.removeClass('fade top bottom left right in') // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do // this manually by checking the contents. if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide() } Popover.prototype.hasContent = function () { return this.getTitle() || this.getContent() } Popover.prototype.getContent = function () { var $e = this.$element var o = this.options return $e.attr('data-content') || (typeof o.content == 'function' ? o.content.call($e[0]) : o.content) } Popover.prototype.arrow = function () { return (this.$arrow = this.$arrow || this.tip().find('.arrow')) } // POPOVER PLUGIN DEFINITION // ========================= function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.popover') var options = typeof option == 'object' && option if (!data && /destroy|hide/.test(option)) return if (!data) $this.data('bs.popover', (data = new Popover(this, options))) if (typeof option == 'string') data[option]() }) } var old = $.fn.popover $.fn.popover = Plugin $.fn.popover.Constructor = Popover // POPOVER NO CONFLICT // =================== $.fn.popover.noConflict = function () { $.fn.popover = old return this } }(jQuery); /* ======================================================================== * Bootstrap: scrollspy.js v3.3.7 * http://getbootstrap.com/javascript/#scrollspy * ======================================================================== * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // SCROLLSPY CLASS DEFINITION // ========================== function ScrollSpy(element, options) { this.$body = $(document.body) this.$scrollElement = $(element).is(document.body) ? $(window) : $(element) this.options = $.extend({}, ScrollSpy.DEFAULTS, options) this.selector = (this.options.target || '') + ' .nav li > a' this.offsets = [] this.targets = [] this.activeTarget = null this.scrollHeight = 0 this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this)) this.refresh() this.process() } ScrollSpy.VERSION = '3.3.7' ScrollSpy.DEFAULTS = { offset: 10 } ScrollSpy.prototype.getScrollHeight = function () { return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight) } ScrollSpy.prototype.refresh = function () { var that = this var offsetMethod = 'offset' var offsetBase = 0 this.offsets = [] this.targets = [] this.scrollHeight = this.getScrollHeight() if (!$.isWindow(this.$scrollElement[0])) { offsetMethod = 'position' offsetBase = this.$scrollElement.scrollTop() } this.$body .find(this.selector) .map(function () { var $el = $(this) var href = $el.data('target') || $el.attr('href') var $href = /^#./.test(href) && $(href) return ($href && $href.length && $href.is(':visible') && [[$href[offsetMethod]().top + offsetBase, href]]) || null }) .sort(function (a, b) { return a[0] - b[0] }) .each(function () { that.offsets.push(this[0]) that.targets.push(this[1]) }) } ScrollSpy.prototype.process = function () { var scrollTop = this.$scrollElement.scrollTop() + this.options.offset var scrollHeight = this.getScrollHeight() var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height() var offsets = this.offsets var targets = this.targets var activeTarget = this.activeTarget var i if (this.scrollHeight != scrollHeight) { this.refresh() } if (scrollTop >= maxScroll) { return activeTarget != (i = targets[targets.length - 1]) && this.activate(i) } if (activeTarget && scrollTop < offsets[0]) { this.activeTarget = null return this.clear() } for (i = offsets.length; i--;) { activeTarget != targets[i] && scrollTop >= offsets[i] && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1]) && this.activate(targets[i]) } } ScrollSpy.prototype.activate = function (target) { this.activeTarget = target this.clear() var selector = this.selector + '[data-target="' + target + '"],' + this.selector + '[href="' + target + '"]' var active = $(selector) .parents('li') .addClass('active') if (active.parent('.dropdown-menu').length) { active = active .closest('li.dropdown') .addClass('active') } active.trigger('activate.bs.scrollspy') } ScrollSpy.prototype.clear = function () { $(this.selector) .parentsUntil(this.options.target, '.active') .removeClass('active') } // SCROLLSPY PLUGIN DEFINITION // =========================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.scrollspy') var options = typeof option == 'object' && option if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options))) if (typeof option == 'string') data[option]() }) } var old = $.fn.scrollspy $.fn.scrollspy = Plugin $.fn.scrollspy.Constructor = ScrollSpy // SCROLLSPY NO CONFLICT // ===================== $.fn.scrollspy.noConflict = function () { $.fn.scrollspy = old return this } // SCROLLSPY DATA-API // ================== $(window).on('load.bs.scrollspy.data-api', function () { $('[data-spy="scroll"]').each(function () { var $spy = $(this) Plugin.call($spy, $spy.data()) }) }) }(jQuery); /* ======================================================================== * Bootstrap: tab.js v3.3.7 * http://getbootstrap.com/javascript/#tabs * ======================================================================== * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // TAB CLASS DEFINITION // ==================== var Tab = function (element) { // jscs:disable requireDollarBeforejQueryAssignment this.element = $(element) // jscs:enable requireDollarBeforejQueryAssignment } Tab.VERSION = '3.3.7' Tab.TRANSITION_DURATION = 150 Tab.prototype.show = function () { var $this = this.element var $ul = $this.closest('ul:not(.dropdown-menu)') var selector = $this.data('target') if (!selector) { selector = $this.attr('href') selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 } if ($this.parent('li').hasClass('active')) return var $previous = $ul.find('.active:last a') var hideEvent = $.Event('hide.bs.tab', { relatedTarget: $this[0] }) var showEvent = $.Event('show.bs.tab', { relatedTarget: $previous[0] }) $previous.trigger(hideEvent) $this.trigger(showEvent) if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return var $target = $(selector) this.activate($this.closest('li'), $ul) this.activate($target, $target.parent(), function () { $previous.trigger({ type: 'hidden.bs.tab', relatedTarget: $this[0] }) $this.trigger({ type: 'shown.bs.tab', relatedTarget: $previous[0] }) }) } Tab.prototype.activate = function (element, container, callback) { var $active = container.find('> .active') var transition = callback && $.support.transition && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length) function next() { $active .removeClass('active') .find('> .dropdown-menu > .active') .removeClass('active') .end() .find('[data-toggle="tab"]') .attr('aria-expanded', false) element .addClass('active') .find('[data-toggle="tab"]') .attr('aria-expanded', true) if (transition) { element[0].offsetWidth // reflow for transition element.addClass('in') } else { element.removeClass('fade') } if (element.parent('.dropdown-menu').length) { element .closest('li.dropdown') .addClass('active') .end() .find('[data-toggle="tab"]') .attr('aria-expanded', true) } callback && callback() } $active.length && transition ? $active .one('bsTransitionEnd', next) .emulateTransitionEnd(Tab.TRANSITION_DURATION) : next() $active.removeClass('in') } // TAB PLUGIN DEFINITION // ===================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.tab') if (!data) $this.data('bs.tab', (data = new Tab(this))) if (typeof option == 'string') data[option]() }) } var old = $.fn.tab $.fn.tab = Plugin $.fn.tab.Constructor = Tab // TAB NO CONFLICT // =============== $.fn.tab.noConflict = function () { $.fn.tab = old return this } // TAB DATA-API // ============ var clickHandler = function (e) { e.preventDefault() Plugin.call($(this), 'show') } $(document) .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler) .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler) }(jQuery); /* ======================================================================== * Bootstrap: affix.js v3.3.7 * http://getbootstrap.com/javascript/#affix * ======================================================================== * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // AFFIX CLASS DEFINITION // ====================== var Affix = function (element, options) { this.options = $.extend({}, Affix.DEFAULTS, options) this.$target = $(this.options.target) .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this)) .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this)) this.$element = $(element) this.affixed = null this.unpin = null this.pinnedOffset = null this.checkPosition() } Affix.VERSION = '3.3.7' Affix.RESET = 'affix affix-top affix-bottom' Affix.DEFAULTS = { offset: 0, target: window } Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) { var scrollTop = this.$target.scrollTop() var position = this.$element.offset() var targetHeight = this.$target.height() if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false if (this.affixed == 'bottom') { if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom' return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom' } var initializing = this.affixed == null var colliderTop = initializing ? scrollTop : position.top var colliderHeight = initializing ? targetHeight : height if (offsetTop != null && scrollTop <= offsetTop) return 'top' if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom' return false } Affix.prototype.getPinnedOffset = function () { if (this.pinnedOffset) return this.pinnedOffset this.$element.removeClass(Affix.RESET).addClass('affix') var scrollTop = this.$target.scrollTop() var position = this.$element.offset() return (this.pinnedOffset = position.top - scrollTop) } Affix.prototype.checkPositionWithEventLoop = function () { setTimeout($.proxy(this.checkPosition, this), 1) } Affix.prototype.checkPosition = function () { if (!this.$element.is(':visible')) return var height = this.$element.height() var offset = this.options.offset var offsetTop = offset.top var offsetBottom = offset.bottom var scrollHeight = Math.max($(document).height(), $(document.body).height()) if (typeof offset != 'object') offsetBottom = offsetTop = offset if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element) if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element) var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom) if (this.affixed != affix) { if (this.unpin != null) this.$element.css('top', '') var affixType = 'affix' + (affix ? '-' + affix : '') var e = $.Event(affixType + '.bs.affix') this.$element.trigger(e) if (e.isDefaultPrevented()) return this.affixed = affix this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null this.$element .removeClass(Affix.RESET) .addClass(affixType) .trigger(affixType.replace('affix', 'affixed') + '.bs.affix') } if (affix == 'bottom') { this.$element.offset({ top: scrollHeight - height - offsetBottom }) } } // AFFIX PLUGIN DEFINITION // ======================= function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.affix') var options = typeof option == 'object' && option if (!data) $this.data('bs.affix', (data = new Affix(this, options))) if (typeof option == 'string') data[option]() }) } var old = $.fn.affix $.fn.affix = Plugin $.fn.affix.Constructor = Affix // AFFIX NO CONFLICT // ================= $.fn.affix.noConflict = function () { $.fn.affix = old return this } // AFFIX DATA-API // ============== $(window).on('load', function () { $('[data-spy="affix"]').each(function () { var $spy = $(this) var data = $spy.data() data.offset = data.offset || {} if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom if (data.offsetTop != null) data.offset.top = data.offsetTop Plugin.call($spy, data) }) }) }(jQuery); ================================================ FILE: static/js/d3.layout.cloud.js ================================================ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g=(g.d3||(g.d3 = {}));g=(g.layout||(g.layout = {}));g.cloud = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o> 5, ch = 1 << 11; module.exports = function() { var size = [256, 256], text = cloudText, font = cloudFont, fontSize = cloudFontSize, fontStyle = cloudFontNormal, fontWeight = cloudFontNormal, rotate = cloudRotate, padding = cloudPadding, spiral = archimedeanSpiral, words = [], timeInterval = Infinity, event = dispatch("word", "end"), timer = null, random = Math.random, cloud = {}, canvas = cloudCanvas; cloud.canvas = function(_) { return arguments.length ? (canvas = functor(_), cloud) : canvas; }; cloud.start = function() { var contextAndRatio = getContext(canvas()), board = zeroArray((size[0] >> 5) * size[1]), bounds = null, n = words.length, i = -1, tags = [], data = words.map(function(d, i) { d.text = text.call(this, d, i); d.font = font.call(this, d, i); d.style = fontStyle.call(this, d, i); d.weight = fontWeight.call(this, d, i); d.rotate = rotate.call(this, d, i); d.size = ~~fontSize.call(this, d, i); d.padding = padding.call(this, d, i); return d; }).sort(function(a, b) { return b.size - a.size; }); if (timer) clearInterval(timer); timer = setInterval(step, 0); step(); return cloud; function step() { var start = Date.now(); while (Date.now() - start < timeInterval && ++i < n && timer) { var d = data[i]; d.x = (size[0] * (random() + .5)) >> 1; d.y = (size[1] * (random() + .5)) >> 1; cloudSprite(contextAndRatio, d, data, i); if (d.hasText && place(board, d, bounds)) { tags.push(d); event.call("word", cloud, d); if (bounds) cloudBounds(bounds, d); else bounds = [{x: d.x + d.x0, y: d.y + d.y0}, {x: d.x + d.x1, y: d.y + d.y1}]; // Temporary hack d.x -= size[0] >> 1; d.y -= size[1] >> 1; } } if (i >= n) { cloud.stop(); event.call("end", cloud, tags, bounds); } } } cloud.stop = function() { if (timer) { clearInterval(timer); timer = null; } return cloud; }; function getContext(canvas) { canvas.width = canvas.height = 1; var ratio = Math.sqrt(canvas.getContext("2d").getImageData(0, 0, 1, 1).data.length >> 2); canvas.width = (cw << 5) / ratio; canvas.height = ch / ratio; var context = canvas.getContext("2d"); context.fillStyle = context.strokeStyle = "red"; context.textAlign = "center"; return {context: context, ratio: ratio}; } function place(board, tag, bounds) { var perimeter = [{x: 0, y: 0}, {x: size[0], y: size[1]}], startX = tag.x, startY = tag.y, maxDelta = Math.sqrt(size[0] * size[0] + size[1] * size[1]), s = spiral(size), dt = random() < .5 ? 1 : -1, t = -dt, dxdy, dx, dy; while (dxdy = s(t += dt)) { dx = ~~dxdy[0]; dy = ~~dxdy[1]; if (Math.min(Math.abs(dx), Math.abs(dy)) >= maxDelta) break; tag.x = startX + dx; tag.y = startY + dy; if (tag.x + tag.x0 < 0 || tag.y + tag.y0 < 0 || tag.x + tag.x1 > size[0] || tag.y + tag.y1 > size[1]) continue; // TODO only check for collisions within current bounds. if (!bounds || !cloudCollide(tag, board, size[0])) { if (!bounds || collideRects(tag, bounds)) { var sprite = tag.sprite, w = tag.width >> 5, sw = size[0] >> 5, lx = tag.x - (w << 4), sx = lx & 0x7f, msx = 32 - sx, h = tag.y1 - tag.y0, x = (tag.y + tag.y0) * sw + (lx >> 5), last; for (var j = 0; j < h; j++) { last = 0; for (var i = 0; i <= w; i++) { board[x + i] |= (last << msx) | (i < w ? (last = sprite[j * w + i]) >>> sx : 0); } x += sw; } delete tag.sprite; return true; } } } return false; } cloud.timeInterval = function(_) { return arguments.length ? (timeInterval = _ == null ? Infinity : _, cloud) : timeInterval; }; cloud.words = function(_) { return arguments.length ? (words = _, cloud) : words; }; cloud.size = function(_) { return arguments.length ? (size = [+_[0], +_[1]], cloud) : size; }; cloud.font = function(_) { return arguments.length ? (font = functor(_), cloud) : font; }; cloud.fontStyle = function(_) { return arguments.length ? (fontStyle = functor(_), cloud) : fontStyle; }; cloud.fontWeight = function(_) { return arguments.length ? (fontWeight = functor(_), cloud) : fontWeight; }; cloud.rotate = function(_) { return arguments.length ? (rotate = functor(_), cloud) : rotate; }; cloud.text = function(_) { return arguments.length ? (text = functor(_), cloud) : text; }; cloud.spiral = function(_) { return arguments.length ? (spiral = spirals[_] || _, cloud) : spiral; }; cloud.fontSize = function(_) { return arguments.length ? (fontSize = functor(_), cloud) : fontSize; }; cloud.padding = function(_) { return arguments.length ? (padding = functor(_), cloud) : padding; }; cloud.random = function(_) { return arguments.length ? (random = _, cloud) : random; }; cloud.on = function() { var value = event.on.apply(event, arguments); return value === event ? cloud : value; }; return cloud; }; function cloudText(d) { return d.text; } function cloudFont() { return "serif"; } function cloudFontNormal() { return "normal"; } function cloudFontSize(d) { return Math.sqrt(d.value); } function cloudRotate() { return (~~(Math.random() * 6) - 3) * 30; } function cloudPadding() { return 1; } // Fetches a monochrome sprite bitmap for the specified text. // Load in batches for speed. function cloudSprite(contextAndRatio, d, data, di) { if (d.sprite) return; var c = contextAndRatio.context, ratio = contextAndRatio.ratio; c.clearRect(0, 0, (cw << 5) / ratio, ch / ratio); var x = 0, y = 0, maxh = 0, n = data.length; --di; while (++di < n) { d = data[di]; c.save(); c.font = d.style + " " + d.weight + " " + ~~((d.size + 1) / ratio) + "px " + d.font; var w = c.measureText(d.text + "m").width * ratio, h = d.size << 1; if (d.rotate) { var sr = Math.sin(d.rotate * cloudRadians), cr = Math.cos(d.rotate * cloudRadians), wcr = w * cr, wsr = w * sr, hcr = h * cr, hsr = h * sr; w = (Math.max(Math.abs(wcr + hsr), Math.abs(wcr - hsr)) + 0x1f) >> 5 << 5; h = ~~Math.max(Math.abs(wsr + hcr), Math.abs(wsr - hcr)); } else { w = (w + 0x1f) >> 5 << 5; } if (h > maxh) maxh = h; if (x + w >= (cw << 5)) { x = 0; y += maxh; maxh = 0; } if (y + h >= ch) break; c.translate((x + (w >> 1)) / ratio, (y + (h >> 1)) / ratio); if (d.rotate) c.rotate(d.rotate * cloudRadians); c.fillText(d.text, 0, 0); if (d.padding) c.lineWidth = 2 * d.padding, c.strokeText(d.text, 0, 0); c.restore(); d.width = w; d.height = h; d.xoff = x; d.yoff = y; d.x1 = w >> 1; d.y1 = h >> 1; d.x0 = -d.x1; d.y0 = -d.y1; d.hasText = true; x += w; } var pixels = c.getImageData(0, 0, (cw << 5) / ratio, ch / ratio).data, sprite = []; while (--di >= 0) { d = data[di]; if (!d.hasText) continue; var w = d.width, w32 = w >> 5, h = d.y1 - d.y0; // Zero the buffer for (var i = 0; i < h * w32; i++) sprite[i] = 0; x = d.xoff; if (x == null) return; y = d.yoff; var seen = 0, seenRow = -1; for (var j = 0; j < h; j++) { for (var i = 0; i < w; i++) { var k = w32 * j + (i >> 5), m = pixels[((y + j) * (cw << 5) + (x + i)) << 2] ? 1 << (31 - (i % 32)) : 0; sprite[k] |= m; seen |= m; } if (seen) seenRow = j; else { d.y0++; h--; j--; y++; } } d.y1 = d.y0 + seenRow; d.sprite = sprite.slice(0, (d.y1 - d.y0) * w32); } } // Use mask-based collision detection. function cloudCollide(tag, board, sw) { sw >>= 5; var sprite = tag.sprite, w = tag.width >> 5, lx = tag.x - (w << 4), sx = lx & 0x7f, msx = 32 - sx, h = tag.y1 - tag.y0, x = (tag.y + tag.y0) * sw + (lx >> 5), last; for (var j = 0; j < h; j++) { last = 0; for (var i = 0; i <= w; i++) { if (((last << msx) | (i < w ? (last = sprite[j * w + i]) >>> sx : 0)) & board[x + i]) return true; } x += sw; } return false; } function cloudBounds(bounds, d) { var b0 = bounds[0], b1 = bounds[1]; if (d.x + d.x0 < b0.x) b0.x = d.x + d.x0; if (d.y + d.y0 < b0.y) b0.y = d.y + d.y0; if (d.x + d.x1 > b1.x) b1.x = d.x + d.x1; if (d.y + d.y1 > b1.y) b1.y = d.y + d.y1; } function collideRects(a, b) { return a.x + a.x1 > b[0].x && a.x + a.x0 < b[1].x && a.y + a.y1 > b[0].y && a.y + a.y0 < b[1].y; } function archimedeanSpiral(size) { var e = size[0] / size[1]; return function(t) { return [e * (t *= .1) * Math.cos(t), t * Math.sin(t)]; }; } function rectangularSpiral(size) { var dy = 4, dx = dy * size[0] / size[1], x = 0, y = 0; return function(t) { var sign = t < 0 ? -1 : 1; // See triangular numbers: T_n = n * (n + 1) / 2. switch ((Math.sqrt(1 + 4 * sign * t) - sign) & 3) { case 0: x += dx; break; case 1: y += dy; break; case 2: x -= dx; break; default: y -= dy; break; } return [x, y]; }; } // TODO reuse arrays? function zeroArray(n) { var a = [], i = -1; while (++i < n) a[i] = 0; return a; } function cloudCanvas() { return document.createElement("canvas"); } function functor(d) { return typeof d === "function" ? d : function() { return d; }; } var spirals = { archimedean: archimedeanSpiral, rectangular: rectangularSpiral }; },{"d3-dispatch":2}],2:[function(require,module,exports){ // https://d3js.org/d3-dispatch/ Version 1.0.2. Copyright 2016 Mike Bostock. (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (factory((global.d3 = global.d3 || {}))); }(this, (function (exports) { 'use strict'; var noop = {value: function() {}}; function dispatch() { for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) { if (!(t = arguments[i] + "") || (t in _)) throw new Error("illegal type: " + t); _[t] = []; } return new Dispatch(_); } function Dispatch(_) { this._ = _; } function parseTypenames(typenames, types) { return typenames.trim().split(/^|\s+/).map(function(t) { var name = "", i = t.indexOf("."); if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i); if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t); return {type: t, name: name}; }); } Dispatch.prototype = dispatch.prototype = { constructor: Dispatch, on: function(typename, callback) { var _ = this._, T = parseTypenames(typename + "", _), t, i = -1, n = T.length; // If no callback was specified, return the callback of the given type and name. if (arguments.length < 2) { while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t; return; } // If a type was specified, set the callback for the given type and name. // Otherwise, if a null callback was specified, remove callbacks of the given name. if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback); while (++i < n) { if (t = (typename = T[i]).type) _[t] = set(_[t], typename.name, callback); else if (callback == null) for (t in _) _[t] = set(_[t], typename.name, null); } return this; }, copy: function() { var copy = {}, _ = this._; for (var t in _) copy[t] = _[t].slice(); return new Dispatch(copy); }, call: function(type, that) { if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2]; if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type); for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args); }, apply: function(type, that, args) { if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type); for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args); } }; function get(type, name) { for (var i = 0, n = type.length, c; i < n; ++i) { if ((c = type[i]).name === name) { return c.value; } } } function set(type, name, callback) { for (var i = 0, n = type.length; i < n; ++i) { if (type[i].name === name) { type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1)); break; } } if (callback != null) type.push({name: name, value: callback}); return type; } exports.dispatch = dispatch; Object.defineProperty(exports, '__esModule', { value: true }); }))); },{}]},{},[1])(1) }); ================================================ FILE: static/js/explorer.js ================================================ class ByteView { constructor(buffer) { this.buffer = buffer; this.view = new DataView(buffer.buffer); this.pos = 0; } getFloat32() { const float = this.view.getFloat32(this.pos); this.pos += 4; return float; } getFloat64() { const float = this.view.getFloat64(this.pos); this.pos += 8; return float; } getBytes(n) { const bytes = this.buffer.subarray(this.pos, this.pos + n); this.pos += n; return bytes; } getByte() { return this.getUint8(); } getUint8() { const uint8 = this.view.getUint8(this.pos); this.pos += 1; return uint8; } getInt8() { const int8 = this.view.getInt8(this.pos); this.pos += 1; return int8; } getUint16() { const uint16 = this.view.getUint16(this.pos); this.pos += 2; return uint16; } getInt16() { const int16 = this.view.getInt16(this.pos); this.pos += 2; return int16; } getUint32() { const uint32 = this.view.getUint32(this.pos); this.pos += 4; return uint32; } getInt32() { const int32 = this.view.getInt32(this.pos); this.pos += 4; return int32; } } // https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 function nextPow2(_v) { let v = _v | 0; v -= 1; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v += 1; return v; } const FORMAT_CHARS = { x: 1, c: 1, b: 1, B: 1, "?": 1, h: 2, H: 2, i: 4, I: 4, l: 4, L: 4, q: 8, Q: 8, e: 2, f: 4, d: 8 }; const SEARCHABLE_FIELDS = new Set([ "category", "company", "country", "host_tld", "month", "site", "tracker" ]); function sizeOfFormat(format) { let size = 0; for (let i = 0; i < format.length; i += 1) { const numberOfBytes = FORMAT_CHARS[format[i]]; if (numberOfBytes === undefined) { throw new Error(`Invalid format character ${format[i]}`); } size += numberOfBytes; } return size; } /* eslint-enable no-bitwise */ class Table { constructor(name) { this.name = name; this.dataTable = null; this.totalRows = 0; // Callback used to request more data from lazy loader this.onRequestMoreRows = () => {}; } update(headers, data) { if (this.dataTable === null) { this.dataTable = $(`#${this.name}`).DataTable({ colReorder: true, columns: headers.map(h => ({ title: h, className: 'order-columns', searchable: SEARCHABLE_FIELDS.has(h) })), scrollX: true, deferRender: true, processing: true, search: false, pagingType: "full_numbers", pageLength: 25, display: true, compact: true, columnDefs: [ { targets: -1, className: 'dt-body-right' } ] }); // When clicking on a row, open page on WTM's website // $(`#${this.name} tbody`).on("click", "tr", function() { // var target = dataTable.row(this).data()[2]; // let url; // if (name === "trackers") { // url = `https://whotracks.me/trackers/${target}.html`; // } else if (name === "sites") { // url = `https://whotracks.me/websites/${target}.html`; // } // if (url !== undefined) { // window.open(url, "_blank"); // } // }); } this.totalRows += data.length; this.dataTable.rows.add(data); this.dataTable.draw("full-hold"); if (this.totalRows < 10000000) { return this.onRequestMoreRows(); } } } class LazyCSVReader { constructor(url) { this.url = url; this.offset = 0; // Initial chunkSize, which should be pretty small to allow for fast first print. this.chunkSize = 32768; this.fetchedRows = 0; this.metadataSize = null; this.numberOfRows = null; this.headers = null; this.symbols = null; this.formatString = null; this.formatSize = null; this.partial = new Uint8Array(); this.ondata = () => {}; } decodeMetadataSize(view) { if (this.metadataSize === null) { this.metadataSize = view.getUint32(); // We make sure that the next request we make can contain all the metadata this.chunkSize = nextPow2(this.metadataSize); } } decodeHeaders(view) { if (this.headers === null) { const numberOfHeaders = view.getUint32(); const headers = []; for (let i = 0; i < numberOfHeaders; i += 1) { const headerSize = view.getUint32(); headers.push( String.fromCharCode.apply(null, view.getBytes(headerSize)) ); } this.headers = headers; } } decodeFormatString(view) { if (this.formatString === null) { const formatStringSize = view.getUint32(); const formatString = String.fromCharCode .apply(null, view.getBytes(formatStringSize)) .slice(1); const formatSize = sizeOfFormat(formatString); this.formatString = formatString; this.formatSize = formatSize; } } decodeSymbols(view) { if (this.symbols === null) { const symbols = new Map(); const numberOfTables = view.getUint32(); for (let i = 0; i < numberOfTables; i += 1) { const headerIndex = view.getUint32(); const tableSize = view.getUint32(); for (let j = 0; j < tableSize; j += 1) { const symbolSize = view.getUint32(); if (!symbols.has(headerIndex)) { symbols.set(headerIndex, new Map()); } symbols .get(headerIndex) .set(j, String.fromCharCode.apply(null, view.getBytes(symbolSize))); } } this.symbols = symbols; } } decodeNumberOfRows(view) { if (this.numberOfRows === null) { this.numberOfRows = view.getUint32(); } } decodeRows(view) { const remainingBytes = view.buffer.byteLength - view.pos; const numberOfRows = Math.floor(remainingBytes / this.formatSize); const rows = []; for (let i = 0; i < numberOfRows; i += 1) { const row = []; for (let j = 0, len = this.formatString.length; j < len; j += 1) { switch (this.formatString[j]) { case "c": { row.push(view.getByte()); break; } case "b": { row.push(view.getInt8()); break; } case "B": { row.push(view.getUint8()); break; } case "h": { row.push(view.getInt16()); break; } case "H": { row.push(view.getUint16()); break; } case "i": { row.push(view.getInt32()); break; } case "I": { row.push(view.getUint32()); break; } case "f": { row.push(view.getFloat32()); break; } case "d": { row.push(view.getFloat64()); break; } default: { throw new Error(`Did not expect defaults: ${this.formatString[j]}`); } } } // Replace symbols by their values this.symbols.forEach((table, headerIndex) => { row[headerIndex] = table.get(row[headerIndex]); }); rows.push(row); } this.partial = view.buffer.subarray(view.pos); this.fetchedRows += rows.length; // As soon as we loaded more than 100 rows (which is the maximum page size // for a DataTable), we increase the chunkSize to allow loading of more rows // at a time, in the background. if (this.fetchedRows > 100) { this.chunkSize = 2097152; } return rows; } async fetchNext() { if (this.fetchedRows === this.numberOfRows) { return; } const response = await fetch(this.url, { headers: { Range: `bytes=${this.offset}-${this.offset + this.chunkSize - 1}` } }); if (response.ok) { let responseBuffer = new Uint8Array(await response.arrayBuffer()); this.offset += responseBuffer.byteLength; const bytes = new Uint8Array( this.partial.byteLength + responseBuffer.byteLength ); bytes.set(this.partial, 0); bytes.set(responseBuffer, this.partial.byteLength); const view = new ByteView(bytes); let beforeOffset = 0; try { this.decodeMetadataSize(view); beforeOffset = view.pos; this.decodeHeaders(view); beforeOffset = view.pos; this.decodeFormatString(view); beforeOffset = view.pos; this.decodeSymbols(view); beforeOffset = view.pos; this.decodeNumberOfRows(view); } catch (ex) { this.partial = view.buffer.subarray(beforeOffset); return this.fetchNext(); } // this.decodeRows(view); return this.ondata(this.decodeRows(view)); } } } window.onload = async () => { ["trackers", "sites", "companies", "sites_trackers"].forEach( type => { const table = new Table(type); const loader = new LazyCSVReader( `${window.location.origin}/data/packed/${type}.pack` ); table.onRequestMoreRows = () => loader.fetchNext(); loader.ondata = data => table.update(loader.headers, data); if ($(`#${type}`).is(":visible")) { // Start loading for visible table loader.fetchNext(); } else { // Start loading whenever the tab is clicked $(`#li-${type}`).click(() => { loader.fetchNext(); $(`#li-${type}`).off(); }); } } ); }; ================================================ FILE: static/js/ghostery.js ================================================ const navbar = document.querySelector('.navbar-ghostery'); if (navbar) { if (window.location.href.indexOf('utm_campaign=WTM_explainer') > -1) { navbar.classList.add('open'); } navbar.querySelector('button').addEventListener('click', () => { navbar.classList.toggle('open'); }); } ================================================ FILE: static/js/highlight.pack.js ================================================ /*! highlight.js v9.12.0 | BSD3 License | git.io/hljslicense */ !function(e){var n="object"==typeof window&&window||"object"==typeof self&&self;"undefined"!=typeof exports?e(exports):n&&(n.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return n.hljs}))}(function(e){function n(e){return e.replace(/&/g,"&").replace(//g,">")}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0===t.index}function a(e){return k.test(e)}function i(e){var n,t,r,i,o=e.className+" ";if(o+=e.parentNode?e.parentNode.className:"",t=B.exec(o))return w(t[1])?t[1]:"no-highlight";for(o=o.split(/\s+/),n=0,r=o.length;r>n;n++)if(i=o[n],a(i)||w(i))return i}function o(e){var n,t={},r=Array.prototype.slice.call(arguments,1);for(n in e)t[n]=e[n];return r.forEach(function(e){for(n in e)t[n]=e[n]}),t}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3===i.nodeType?a+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!==r[0].offset?e[0].offset"}function u(e){s+=""}function c(e){("start"===e.event?o:u)(e.node)}for(var l=0,s="",f=[];e.length||r.length;){var g=i();if(s+=n(a.substring(l,g[0].offset)),l=g[0].offset,g===e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g===e&&g.length&&g[0].offset===l);f.reverse().forEach(o)}else"start"===g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return s+n(a.substr(l))}function l(e){return e.v&&!e.cached_variants&&(e.cached_variants=e.v.map(function(n){return o(e,{v:null},n)})),e.cached_variants||e.eW&&[o(e)]||[e]}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var o={},u=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");o[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?u("keyword",a.k):x(a.k).forEach(function(e){u(e,a.k[e])}),a.k=o}a.lR=t(a.l||/\w+/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),null==a.r&&(a.r=1),a.c||(a.c=[]),a.c=Array.prototype.concat.apply([],a.c.map(function(e){return l("self"===e?a:e)})),a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var c=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=c.length?t(c.join("|"),!0):{exec:function(){return null}}}}r(e)}function f(e,t,a,i){function o(e,n){var t,a;for(t=0,a=n.c.length;a>t;t++)if(r(n.c[t].bR,e))return n.c[t]}function u(e,n){if(r(e.eR,n)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?u(e.parent,n):void 0}function c(e,n){return!a&&r(n.iR,e)}function l(e,n){var t=N.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function p(e,n,t,r){var a=r?"":I.classPrefix,i='',i+n+o}function h(){var e,t,r,a;if(!E.k)return n(k);for(a="",t=0,E.lR.lastIndex=0,r=E.lR.exec(k);r;)a+=n(k.substring(t,r.index)),e=l(E,r),e?(B+=e[1],a+=p(e[0],n(r[0]))):a+=n(r[0]),t=E.lR.lastIndex,r=E.lR.exec(k);return a+n(k.substr(t))}function d(){var e="string"==typeof E.sL;if(e&&!y[E.sL])return n(k);var t=e?f(E.sL,k,!0,x[E.sL]):g(k,E.sL.length?E.sL:void 0);return E.r>0&&(B+=t.r),e&&(x[E.sL]=t.top),p(t.language,t.value,!1,!0)}function b(){L+=null!=E.sL?d():h(),k=""}function v(e){L+=e.cN?p(e.cN,"",!0):"",E=Object.create(e,{parent:{value:E}})}function m(e,n){if(k+=e,null==n)return b(),0;var t=o(n,E);if(t)return t.skip?k+=n:(t.eB&&(k+=n),b(),t.rB||t.eB||(k=n)),v(t,n),t.rB?0:n.length;var r=u(E,n);if(r){var a=E;a.skip?k+=n:(a.rE||a.eE||(k+=n),b(),a.eE&&(k=n));do E.cN&&(L+=C),E.skip||(B+=E.r),E=E.parent;while(E!==r.parent);return r.starts&&v(r.starts,""),a.rE?0:n.length}if(c(n,E))throw new Error('Illegal lexeme "'+n+'" for mode "'+(E.cN||"")+'"');return k+=n,n.length||1}var N=w(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var R,E=i||N,x={},L="";for(R=E;R!==N;R=R.parent)R.cN&&(L=p(R.cN,"",!0)+L);var k="",B=0;try{for(var M,j,O=0;;){if(E.t.lastIndex=O,M=E.t.exec(t),!M)break;j=m(t.substring(O,M.index),M[0]),O=M.index+j}for(m(t.substr(O)),R=E;R.parent;R=R.parent)R.cN&&(L+=C);return{r:B,value:L,language:e,top:E}}catch(T){if(T.message&&-1!==T.message.indexOf("Illegal"))return{r:0,value:n(t)};throw T}}function g(e,t){t=t||I.languages||x(y);var r={r:0,value:n(e)},a=r;return t.filter(w).forEach(function(n){var t=f(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}),a.language&&(r.second_best=a),r}function p(e){return I.tabReplace||I.useBR?e.replace(M,function(e,n){return I.useBR&&"\n"===e?"
":I.tabReplace?n.replace(/\t/g,I.tabReplace):""}):e}function h(e,n,t){var r=n?L[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function d(e){var n,t,r,o,l,s=i(e);a(s)||(I.useBR?(n=document.createElementNS("http://www.w3.org/1999/xhtml","div"),n.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n")):n=e,l=n.textContent,r=s?f(s,l,!0):g(l),t=u(n),t.length&&(o=document.createElementNS("http://www.w3.org/1999/xhtml","div"),o.innerHTML=r.value,r.value=c(t,u(o),l)),r.value=p(r.value),e.innerHTML=r.value,e.className=h(e.className,s,r.language),e.result={language:r.language,re:r.r},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.r}))}function b(e){I=o(I,e)}function v(){if(!v.called){v.called=!0;var e=document.querySelectorAll("pre code");E.forEach.call(e,d)}}function m(){addEventListener("DOMContentLoaded",v,!1),addEventListener("load",v,!1)}function N(n,t){var r=y[n]=t(e);r.aliases&&r.aliases.forEach(function(e){L[e]=n})}function R(){return x(y)}function w(e){return e=(e||"").toLowerCase(),y[e]||y[L[e]]}var E=[],x=Object.keys,y={},L={},k=/^(no-?highlight|plain|text)$/i,B=/\blang(?:uage)?-([\w-]+)\b/i,M=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,C="
",I={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0};return e.highlight=f,e.highlightAuto=g,e.fixMarkup=p,e.highlightBlock=d,e.configure=b,e.initHighlighting=v,e.initHighlightingOnLoad=m,e.registerLanguage=N,e.listLanguages=R,e.getLanguage=w,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e.METHOD_GUARD={b:"\\.\\s*"+e.UIR,r:0},e});hljs.registerLanguage("xml",function(s){var e="[A-Za-z0-9\\._:-]+",t={eW:!0,i:/`]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist"],cI:!0,c:[{cN:"meta",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},s.C("",{r:10}),{b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{b:/<\?(php)?/,e:/\?>/,sL:"php",c:[{b:"/\\*",e:"\\*/",skip:!0}]},{cN:"tag",b:"|$)",e:">",k:{name:"style"},c:[t],starts:{e:"",rE:!0,sL:["css","xml"]}},{cN:"tag",b:"|$)",e:">",k:{name:"script"},c:[t],starts:{e:"",rE:!0,sL:["actionscript","javascript","handlebars","xml"]}},{cN:"meta",v:[{b:/<\?xml/,e:/\?>/,r:10},{b:/<\?\w+/,e:/\?>/}]},{cN:"tag",b:"",c:[{cN:"name",b:/[^\/><\s]+/,r:0},t]}]}});hljs.registerLanguage("css",function(e){var c="[a-zA-Z-][a-zA-Z0-9_-]*",t={b:/[A-Z\_\.\-]+\s*:/,rB:!0,e:";",eW:!0,c:[{cN:"attribute",b:/\S/,e:":",eE:!0,starts:{eW:!0,eE:!0,c:[{b:/[\w-]+\(/,rB:!0,c:[{cN:"built_in",b:/[\w-]+/},{b:/\(/,e:/\)/,c:[e.ASM,e.QSM]}]},e.CSSNM,e.QSM,e.ASM,e.CBCM,{cN:"number",b:"#[0-9A-Fa-f]+"},{cN:"meta",b:"!important"}]}}]};return{cI:!0,i:/[=\/|'\$]/,c:[e.CBCM,{cN:"selector-id",b:/#[A-Za-z0-9_-]+/},{cN:"selector-class",b:/\.[A-Za-z0-9_-]+/},{cN:"selector-attr",b:/\[/,e:/\]/,i:"$"},{cN:"selector-pseudo",b:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{b:"@(font-face|page)",l:"[a-z-]+",k:"font-face page"},{b:"@",e:"[{;]",i:/:/,c:[{cN:"keyword",b:/\w+/},{b:/\s/,eW:!0,eE:!0,r:0,c:[e.ASM,e.QSM,e.CSSNM]}]},{cN:"selector-tag",b:c,r:0},{b:"{",e:"}",i:/\S/,c:[e.CBCM,t]}]}});hljs.registerLanguage("apache",function(e){var r={cN:"number",b:"[\\$%]\\d+"};return{aliases:["apacheconf"],cI:!0,c:[e.HCM,{cN:"section",b:""},{cN:"attribute",b:/\w+/,r:0,k:{nomarkup:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername"},starts:{e:/$/,r:0,k:{literal:"on off all"},c:[{cN:"meta",b:"\\s\\[",e:"\\]$"},{cN:"variable",b:"[\\$%]\\{",e:"\\}",c:["self",r]},r,e.QSM]}}],i:/\S/}});hljs.registerLanguage("diff",function(e){return{aliases:["patch"],c:[{cN:"meta",r:10,v:[{b:/^@@ +\-\d+,\d+ +\+\d+,\d+ +@@$/},{b:/^\*\*\* +\d+,\d+ +\*\*\*\*$/},{b:/^\-\-\- +\d+,\d+ +\-\-\-\-$/}]},{cN:"comment",v:[{b:/Index: /,e:/$/},{b:/={3,}/,e:/$/},{b:/^\-{3}/,e:/$/},{b:/^\*{3} /,e:/$/},{b:/^\+{3}/,e:/$/},{b:/\*{5}/,e:/\*{5}$/}]},{cN:"addition",b:"^\\+",e:"$"},{cN:"deletion",b:"^\\-",e:"$"},{cN:"addition",b:"^\\!",e:"$"}]}});hljs.registerLanguage("java",function(e){var a="[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*",t=a+"(<"+a+"(\\s*,\\s*"+a+")*>)?",r="false synchronized int abstract float private char boolean static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private module requires exports do",s="\\b(0[bB]([01]+[01_]+[01]+|[01]+)|0[xX]([a-fA-F0-9]+[a-fA-F0-9_]+[a-fA-F0-9]+|[a-fA-F0-9]+)|(([\\d]+[\\d_]+[\\d]+|[\\d]+)(\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))?|\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))([eE][-+]?\\d+)?)[lLfF]?",c={cN:"number",b:s,r:0};return{aliases:["jsp"],k:r,i:/<\/|#/,c:[e.C("/\\*\\*","\\*/",{r:0,c:[{b:/\w+@/,r:0},{cN:"doctag",b:"@[A-Za-z]+"}]}),e.CLCM,e.CBCM,e.ASM,e.QSM,{cN:"class",bK:"class interface",e:/[{;=]/,eE:!0,k:"class interface",i:/[:"\[\]]/,c:[{bK:"extends implements"},e.UTM]},{bK:"new throw return else",r:0},{cN:"function",b:"("+t+"\\s+)+"+e.UIR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:r,c:[{b:e.UIR+"\\s*\\(",rB:!0,r:0,c:[e.UTM]},{cN:"params",b:/\(/,e:/\)/,k:r,r:0,c:[e.ASM,e.QSM,e.CNM,e.CBCM]},e.CLCM,e.CBCM]},c,{cN:"meta",b:"@[A-Za-z]+"}]}});hljs.registerLanguage("python",function(e){var r={keyword:"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda async await nonlocal|10 None True False",built_in:"Ellipsis NotImplemented"},b={cN:"meta",b:/^(>>>|\.\.\.) /},c={cN:"subst",b:/\{/,e:/\}/,k:r,i:/#/},a={cN:"string",c:[e.BE],v:[{b:/(u|b)?r?'''/,e:/'''/,c:[b],r:10},{b:/(u|b)?r?"""/,e:/"""/,c:[b],r:10},{b:/(fr|rf|f)'''/,e:/'''/,c:[b,c]},{b:/(fr|rf|f)"""/,e:/"""/,c:[b,c]},{b:/(u|r|ur)'/,e:/'/,r:10},{b:/(u|r|ur)"/,e:/"/,r:10},{b:/(b|br)'/,e:/'/},{b:/(b|br)"/,e:/"/},{b:/(fr|rf|f)'/,e:/'/,c:[c]},{b:/(fr|rf|f)"/,e:/"/,c:[c]},e.ASM,e.QSM]},s={cN:"number",r:0,v:[{b:e.BNR+"[lLjJ]?"},{b:"\\b(0o[0-7]+)[lLjJ]?"},{b:e.CNR+"[lLjJ]?"}]},i={cN:"params",b:/\(/,e:/\)/,c:["self",b,s,a]};return c.c=[a,s,b],{aliases:["py","gyp"],k:r,i:/(<\/|->|\?)|=>/,c:[b,s,a,e.HCM,{v:[{cN:"function",bK:"def"},{cN:"class",bK:"class"}],e:/:/,i:/[${=;\n,]/,c:[e.UTM,i,{b:/->/,eW:!0,k:"None"}]},{cN:"meta",b:/^[\t ]*@/,e:/$/},{b:/\b(print|exec)\(/}]}});hljs.registerLanguage("javascript",function(e){var r="[A-Za-z$_][0-9A-Za-z$_]*",t={keyword:"in of if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const export super debugger as async await static import from as",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect Promise"},a={cN:"number",v:[{b:"\\b(0[bB][01]+)"},{b:"\\b(0[oO][0-7]+)"},{b:e.CNR}],r:0},n={cN:"subst",b:"\\$\\{",e:"\\}",k:t,c:[]},c={cN:"string",b:"`",e:"`",c:[e.BE,n]};n.c=[e.ASM,e.QSM,c,a,e.RM];var s=n.c.concat([e.CBCM,e.CLCM]);return{aliases:["js","jsx"],k:t,c:[{cN:"meta",r:10,b:/^\s*['"]use (strict|asm)['"]/},{cN:"meta",b:/^#!/,e:/$/},e.ASM,e.QSM,c,e.CLCM,e.CBCM,a,{b:/[{,]\s*/,r:0,c:[{b:r+"\\s*:",rB:!0,r:0,c:[{cN:"attr",b:r,r:0}]}]},{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{cN:"function",b:"(\\(.*?\\)|"+r+")\\s*=>",rB:!0,e:"\\s*=>",c:[{cN:"params",v:[{b:r},{b:/\(\s*\)/},{b:/\(/,e:/\)/,eB:!0,eE:!0,k:t,c:s}]}]},{b://,sL:"xml",c:[{b:/<\w+\s*\/>/,skip:!0},{b:/<\w+/,e:/(\/\w+|\w+\/)>/,skip:!0,c:[{b:/<\w+\s*\/>/,skip:!0},"self"]}]}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:r}),{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,c:s}],i:/\[|%/},{b:/\$[(.]/},e.METHOD_GUARD,{cN:"class",bK:"class",e:/[{;=]/,eE:!0,i:/[:"\[\]]/,c:[{bK:"extends"},e.UTM]},{bK:"constructor",e:/\{/,eE:!0}],i:/#(?!!)/}});hljs.registerLanguage("markdown",function(e){return{aliases:["md","mkdown","mkd"],c:[{cN:"section",v:[{b:"^#{1,6}",e:"$"},{b:"^.+?\\n[=-]{2,}$"}]},{b:"<",e:">",sL:"xml",r:0},{cN:"bullet",b:"^([*+-]|(\\d+\\.))\\s+"},{cN:"strong",b:"[*_]{2}.+?[*_]{2}"},{cN:"emphasis",v:[{b:"\\*.+?\\*"},{b:"_.+?_",r:0}]},{cN:"quote",b:"^>\\s+",e:"$"},{cN:"code",v:[{b:"^```w*s*$",e:"^```s*$"},{b:"`.+?`"},{b:"^( {4}| )",e:"$",r:0}]},{b:"^[-\\*]{3,}",e:"$"},{b:"\\[.+?\\][\\(\\[].*?[\\)\\]]",rB:!0,c:[{cN:"string",b:"\\[",e:"\\]",eB:!0,rE:!0,r:0},{cN:"link",b:"\\]\\(",e:"\\)",eB:!0,eE:!0},{cN:"symbol",b:"\\]\\[",e:"\\]",eB:!0,eE:!0}],r:10},{b:/^\[[^\n]+\]:/,rB:!0,c:[{cN:"symbol",b:/\[/,e:/\]/,eB:!0,eE:!0},{cN:"link",b:/:\s*/,e:/$/,eB:!0}]}]}});hljs.registerLanguage("json",function(e){var i={literal:"true false null"},n=[e.QSM,e.CNM],r={e:",",eW:!0,eE:!0,c:n,k:i},t={b:"{",e:"}",c:[{cN:"attr",b:/"/,e:/"/,c:[e.BE],i:"\\n"},e.inherit(r,{b:/:/})],i:"\\S"},c={b:"\\[",e:"\\]",c:[e.inherit(r)],i:"\\S"};return n.splice(n.length,0,t,c),{c:n,k:i,i:"\\S"}});hljs.registerLanguage("cpp",function(t){var e={cN:"keyword",b:"\\b[a-z\\d_]*_t\\b"},r={cN:"string",v:[{b:'(u8?|U)?L?"',e:'"',i:"\\n",c:[t.BE]},{b:'(u8?|U)?R"',e:'"',c:[t.BE]},{b:"'\\\\?.",e:"'",i:"."}]},s={cN:"number",v:[{b:"\\b(0b[01']+)"},{b:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{b:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],r:0},i={cN:"meta",b:/#\s*[a-z]+\b/,e:/$/,k:{"meta-keyword":"if else elif endif define undef warning error line pragma ifdef ifndef include"},c:[{b:/\\\n/,r:0},t.inherit(r,{cN:"meta-string"}),{cN:"meta-string",b:/<[^\n>]*>/,e:/$/,i:"\\n"},t.CLCM,t.CBCM]},a=t.IR+"\\s*\\(",c={keyword:"int float while private char catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignof constexpr decltype noexcept static_assert thread_local restrict _Bool complex _Complex _Imaginary atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and or not",built_in:"std string cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap array shared_ptr abort abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr",literal:"true false nullptr NULL"},n=[e,t.CLCM,t.CBCM,s,r];return{aliases:["c","cc","h","c++","h++","hpp"],k:c,i:"",k:c,c:["self",e]},{b:t.IR+"::",k:c},{v:[{b:/=/,e:/;/},{b:/\(/,e:/\)/},{bK:"new throw return else",e:/;/}],k:c,c:n.concat([{b:/\(/,e:/\)/,k:c,c:n.concat(["self"]),r:0}]),r:0},{cN:"function",b:"("+t.IR+"[\\*&\\s]+)+"+a,rB:!0,e:/[{;=]/,eE:!0,k:c,i:/[^\w\s\*&]/,c:[{b:a,rB:!0,c:[t.TM],r:0},{cN:"params",b:/\(/,e:/\)/,k:c,r:0,c:[t.CLCM,t.CBCM,r,s,e]},t.CLCM,t.CBCM,i]},{cN:"class",bK:"class struct",e:/[{;:]/,c:[{b://,c:["self"]},t.TM]}]),exports:{preprocessor:i,strings:r,k:c}}});hljs.registerLanguage("perl",function(e){var t="getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qqfileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmgetsub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedirioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when",r={cN:"subst",b:"[$@]\\{",e:"\\}",k:t},s={b:"->{",e:"}"},n={v:[{b:/\$\d/},{b:/[\$%@](\^\w\b|#\w+(::\w+)*|{\w+}|\w+(::\w*)*)/},{b:/[\$%@][^\s\w{]/,r:0}]},i=[e.BE,r,n],o=[n,e.HCM,e.C("^\\=\\w","\\=cut",{eW:!0}),s,{cN:"string",c:i,v:[{b:"q[qwxr]?\\s*\\(",e:"\\)",r:5},{b:"q[qwxr]?\\s*\\[",e:"\\]",r:5},{b:"q[qwxr]?\\s*\\{",e:"\\}",r:5},{b:"q[qwxr]?\\s*\\|",e:"\\|",r:5},{b:"q[qwxr]?\\s*\\<",e:"\\>",r:5},{b:"qw\\s+q",e:"q",r:5},{b:"'",e:"'",c:[e.BE]},{b:'"',e:'"'},{b:"`",e:"`",c:[e.BE]},{b:"{\\w+}",c:[],r:0},{b:"-?\\w+\\s*\\=\\>",c:[],r:0}]},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{b:"(\\/\\/|"+e.RSR+"|\\b(split|return|print|reverse|grep)\\b)\\s*",k:"split return print reverse grep",r:0,c:[e.HCM,{cN:"regexp",b:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",r:10},{cN:"regexp",b:"(m|qr)?/",e:"/[a-z]*",c:[e.BE],r:0}]},{cN:"function",bK:"sub",e:"(\\s*\\(.*?\\))?[;{]",eE:!0,r:5,c:[e.TM]},{b:"-\\w\\b",r:0},{b:"^__DATA__$",e:"^__END__$",sL:"mojolicious",c:[{b:"^@@.*",e:"$",cN:"comment"}]}];return r.c=o,s.c=o,{aliases:["pl","pm"],l:/[\w\.]+/,k:t,c:o}});hljs.registerLanguage("nginx",function(e){var r={cN:"variable",v:[{b:/\$\d+/},{b:/\$\{/,e:/}/},{b:"[\\$\\@]"+e.UIR}]},b={eW:!0,l:"[a-z/_]+",k:{literal:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll"},r:0,i:"=>",c:[e.HCM,{cN:"string",c:[e.BE,r],v:[{b:/"/,e:/"/},{b:/'/,e:/'/}]},{b:"([a-z]+):/",e:"\\s",eW:!0,eE:!0,c:[r]},{cN:"regexp",c:[e.BE,r],v:[{b:"\\s\\^",e:"\\s|{|;",rE:!0},{b:"~\\*?\\s+",e:"\\s|{|;",rE:!0},{b:"\\*(\\.[a-z\\-]+)+"},{b:"([a-z\\-]+\\.)+\\*"}]},{cN:"number",b:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{cN:"number",b:"\\b\\d+[kKmMgGdshdwy]*\\b",r:0},r]};return{aliases:["nginxconf"],c:[e.HCM,{b:e.UIR+"\\s+{",rB:!0,e:"{",c:[{cN:"section",b:e.UIR}],r:0},{b:e.UIR+"\\s",e:";|{",rB:!0,c:[{cN:"attribute",b:e.UIR,starts:b}],r:0}],i:"[^\\s\\}]"}});hljs.registerLanguage("sql",function(e){var t=e.C("--","$");return{cI:!0,i:/[<>{}*#]/,c:[{bK:"begin end start commit rollback savepoint lock alter create drop rename call delete do handler insert load replace select truncate update set show pragma grant merge describe use explain help declare prepare execute deallocate release unlock purge reset change stop analyze cache flush optimize repair kill install uninstall checksum restore check backup revoke comment",e:/;/,eW:!0,l:/[\w\.]+/,k:{keyword:"abort abs absolute acc acce accep accept access accessed accessible account acos action activate add addtime admin administer advanced advise aes_decrypt aes_encrypt after agent aggregate ali alia alias allocate allow alter always analyze ancillary and any anydata anydataset anyschema anytype apply archive archived archivelog are as asc ascii asin assembly assertion associate asynchronous at atan atn2 attr attri attrib attribu attribut attribute attributes audit authenticated authentication authid authors auto autoallocate autodblink autoextend automatic availability avg backup badfile basicfile before begin beginning benchmark between bfile bfile_base big bigfile bin binary_double binary_float binlog bit_and bit_count bit_length bit_or bit_xor bitmap blob_base block blocksize body both bound buffer_cache buffer_pool build bulk by byte byteordermark bytes cache caching call calling cancel capacity cascade cascaded case cast catalog category ceil ceiling chain change changed char_base char_length character_length characters characterset charindex charset charsetform charsetid check checksum checksum_agg child choose chr chunk class cleanup clear client clob clob_base clone close cluster_id cluster_probability cluster_set clustering coalesce coercibility col collate collation collect colu colum column column_value columns columns_updated comment commit compact compatibility compiled complete composite_limit compound compress compute concat concat_ws concurrent confirm conn connec connect connect_by_iscycle connect_by_isleaf connect_by_root connect_time connection consider consistent constant constraint constraints constructor container content contents context contributors controlfile conv convert convert_tz corr corr_k corr_s corresponding corruption cos cost count count_big counted covar_pop covar_samp cpu_per_call cpu_per_session crc32 create creation critical cross cube cume_dist curdate current current_date current_time current_timestamp current_user cursor curtime customdatum cycle data database databases datafile datafiles datalength date_add date_cache date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts day day_to_second dayname dayofmonth dayofweek dayofyear days db_role_change dbtimezone ddl deallocate declare decode decompose decrement decrypt deduplicate def defa defau defaul default defaults deferred defi defin define degrees delayed delegate delete delete_all delimited demand dense_rank depth dequeue des_decrypt des_encrypt des_key_file desc descr descri describ describe descriptor deterministic diagnostics difference dimension direct_load directory disable disable_all disallow disassociate discardfile disconnect diskgroup distinct distinctrow distribute distributed div do document domain dotnet double downgrade drop dumpfile duplicate duration each edition editionable editions element ellipsis else elsif elt empty enable enable_all enclosed encode encoding encrypt end end-exec endian enforced engine engines enqueue enterprise entityescaping eomonth error errors escaped evalname evaluate event eventdata events except exception exceptions exchange exclude excluding execu execut execute exempt exists exit exp expire explain export export_set extended extent external external_1 external_2 externally extract failed failed_login_attempts failover failure far fast feature_set feature_value fetch field fields file file_name_convert filesystem_like_logging final finish first first_value fixed flash_cache flashback floor flush following follows for forall force form forma format found found_rows freelist freelists freepools fresh from from_base64 from_days ftp full function general generated get get_format get_lock getdate getutcdate global global_name globally go goto grant grants greatest group group_concat group_id grouping grouping_id groups gtid_subtract guarantee guard handler hash hashkeys having hea head headi headin heading heap help hex hierarchy high high_priority hosts hour http id ident_current ident_incr ident_seed identified identity idle_time if ifnull ignore iif ilike ilm immediate import in include including increment index indexes indexing indextype indicator indices inet6_aton inet6_ntoa inet_aton inet_ntoa infile initial initialized initially initrans inmemory inner innodb input insert install instance instantiable instr interface interleaved intersect into invalidate invisible is is_free_lock is_ipv4 is_ipv4_compat is_not is_not_null is_used_lock isdate isnull isolation iterate java join json json_exists keep keep_duplicates key keys kill language large last last_day last_insert_id last_value lax lcase lead leading least leaves left len lenght length less level levels library like like2 like4 likec limit lines link list listagg little ln load load_file lob lobs local localtime localtimestamp locate locator lock locked log log10 log2 logfile logfiles logging logical logical_reads_per_call logoff logon logs long loop low low_priority lower lpad lrtrim ltrim main make_set makedate maketime managed management manual map mapping mask master master_pos_wait match matched materialized max maxextents maximize maxinstances maxlen maxlogfiles maxloghistory maxlogmembers maxsize maxtrans md5 measures median medium member memcompress memory merge microsecond mid migration min minextents minimum mining minus minute minvalue missing mod mode model modification modify module monitoring month months mount move movement multiset mutex name name_const names nan national native natural nav nchar nclob nested never new newline next nextval no no_write_to_binlog noarchivelog noaudit nobadfile nocheck nocompress nocopy nocycle nodelay nodiscardfile noentityescaping noguarantee nokeep nologfile nomapping nomaxvalue nominimize nominvalue nomonitoring none noneditionable nonschema noorder nopr nopro noprom nopromp noprompt norely noresetlogs noreverse normal norowdependencies noschemacheck noswitch not nothing notice notrim novalidate now nowait nth_value nullif nulls num numb numbe nvarchar nvarchar2 object ocicoll ocidate ocidatetime ociduration ociinterval ociloblocator ocinumber ociref ocirefcursor ocirowid ocistring ocitype oct octet_length of off offline offset oid oidindex old on online only opaque open operations operator optimal optimize option optionally or oracle oracle_date oradata ord ordaudio orddicom orddoc order ordimage ordinality ordvideo organization orlany orlvary out outer outfile outline output over overflow overriding package pad parallel parallel_enable parameters parent parse partial partition partitions pascal passing password password_grace_time password_lock_time password_reuse_max password_reuse_time password_verify_function patch path patindex pctincrease pctthreshold pctused pctversion percent percent_rank percentile_cont percentile_disc performance period period_add period_diff permanent physical pi pipe pipelined pivot pluggable plugin policy position post_transaction pow power pragma prebuilt precedes preceding precision prediction prediction_cost prediction_details prediction_probability prediction_set prepare present preserve prior priority private private_sga privileges procedural procedure procedure_analyze processlist profiles project prompt protection public publishingservername purge quarter query quick quiesce quota quotename radians raise rand range rank raw read reads readsize rebuild record records recover recovery recursive recycle redo reduced ref reference referenced references referencing refresh regexp_like register regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy reject rekey relational relative relaylog release release_lock relies_on relocate rely rem remainder rename repair repeat replace replicate replication required reset resetlogs resize resource respect restore restricted result result_cache resumable resume retention return returning returns reuse reverse revoke right rlike role roles rollback rolling rollup round row row_count rowdependencies rowid rownum rows rtrim rules safe salt sample save savepoint sb1 sb2 sb4 scan schema schemacheck scn scope scroll sdo_georaster sdo_topo_geometry search sec_to_time second section securefile security seed segment select self sequence sequential serializable server servererror session session_user sessions_per_user set sets settings sha sha1 sha2 share shared shared_pool short show shrink shutdown si_averagecolor si_colorhistogram si_featurelist si_positionalcolor si_stillimage si_texture siblings sid sign sin size size_t sizes skip slave sleep smalldatetimefromparts smallfile snapshot some soname sort soundex source space sparse spfile split sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_small_result sql_variant_property sqlcode sqldata sqlerror sqlname sqlstate sqrt square standalone standby start starting startup statement static statistics stats_binomial_test stats_crosstab stats_ks_test stats_mode stats_mw_test stats_one_way_anova stats_t_test_ stats_t_test_indep stats_t_test_one stats_t_test_paired stats_wsr_test status std stddev stddev_pop stddev_samp stdev stop storage store stored str str_to_date straight_join strcmp strict string struct stuff style subdate subpartition subpartitions substitutable substr substring subtime subtring_index subtype success sum suspend switch switchoffset switchover sync synchronous synonym sys sys_xmlagg sysasm sysaux sysdate sysdatetimeoffset sysdba sysoper system system_user sysutcdatetime table tables tablespace tan tdo template temporary terminated tertiary_weights test than then thread through tier ties time time_format time_zone timediff timefromparts timeout timestamp timestampadd timestampdiff timezone_abbr timezone_minute timezone_region to to_base64 to_date to_days to_seconds todatetimeoffset trace tracking transaction transactional translate translation treat trigger trigger_nestlevel triggers trim truncate try_cast try_convert try_parse type ub1 ub2 ub4 ucase unarchived unbounded uncompress under undo unhex unicode uniform uninstall union unique unix_timestamp unknown unlimited unlock unpivot unrecoverable unsafe unsigned until untrusted unusable unused update updated upgrade upped upper upsert url urowid usable usage use use_stored_outlines user user_data user_resources users using utc_date utc_timestamp uuid uuid_short validate validate_password_strength validation valist value values var var_samp varcharc vari varia variab variabl variable variables variance varp varraw varrawc varray verify version versions view virtual visible void wait wallet warning warnings week weekday weekofyear wellformed when whene whenev wheneve whenever where while whitespace with within without work wrapped xdb xml xmlagg xmlattributes xmlcast xmlcolattval xmlelement xmlexists xmlforest xmlindex xmlnamespaces xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltype xor year year_to_month years yearweek",literal:"true false null",built_in:"array bigint binary bit blob boolean char character date dec decimal float int int8 integer interval number numeric real record serial serial8 smallint text varchar varying void"},c:[{cN:"string",b:"'",e:"'",c:[e.BE,{b:"''"}]},{cN:"string",b:'"',e:'"',c:[e.BE,{b:'""'}]},{cN:"string",b:"`",e:"`",c:[e.BE]},e.CNM,e.CBCM,t]},e.CBCM,t]}});hljs.registerLanguage("coffeescript",function(e){var c={keyword:"in if for while finally new do return else break catch instanceof throw try this switch continue typeof delete debugger super yield import export from as default await then unless until loop of by when and or is isnt not",literal:"true false null undefined yes no on off",built_in:"npm require console print module global window document"},n="[A-Za-z$_][0-9A-Za-z$_]*",r={cN:"subst",b:/#\{/,e:/}/,k:c},i=[e.BNM,e.inherit(e.CNM,{starts:{e:"(\\s*/)?",r:0}}),{cN:"string",v:[{b:/'''/,e:/'''/,c:[e.BE]},{b:/'/,e:/'/,c:[e.BE]},{b:/"""/,e:/"""/,c:[e.BE,r]},{b:/"/,e:/"/,c:[e.BE,r]}]},{cN:"regexp",v:[{b:"///",e:"///",c:[r,e.HCM]},{b:"//[gim]*",r:0},{b:/\/(?![ *])(\\\/|.)*?\/[gim]*(?=\W|$)/}]},{b:"@"+n},{sL:"javascript",eB:!0,eE:!0,v:[{b:"```",e:"```"},{b:"`",e:"`"}]}];r.c=i;var s=e.inherit(e.TM,{b:n}),t="(\\(.*\\))?\\s*\\B[-=]>",o={cN:"params",b:"\\([^\\(]",rB:!0,c:[{b:/\(/,e:/\)/,k:c,c:["self"].concat(i)}]};return{aliases:["coffee","cson","iced"],k:c,i:/\/\*/,c:i.concat([e.C("###","###"),e.HCM,{cN:"function",b:"^\\s*"+n+"\\s*=\\s*"+t,e:"[-=]>",rB:!0,c:[s,o]},{b:/[:\(,=]\s*/,r:0,c:[{cN:"function",b:t,e:"[-=]>",rB:!0,c:[o]}]},{cN:"class",bK:"class",e:"$",i:/[:="\[\]]/,c:[{bK:"extends",eW:!0,i:/[:="\[\]]/,c:[s]},s]},{b:n+":",e:":",rB:!0,rE:!0,r:0}])}});hljs.registerLanguage("bash",function(e){var t={cN:"variable",v:[{b:/\$[\w\d#@][\w\d_]*/},{b:/\$\{(.*?)}/}]},s={cN:"string",b:/"/,e:/"/,c:[e.BE,t,{cN:"variable",b:/\$\(/,e:/\)/,c:[e.BE]}]},a={cN:"string",b:/'/,e:/'/};return{aliases:["sh","zsh"],l:/\b-?[a-z\._]+\b/,k:{keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp",_:"-ne -eq -lt -gt -f -d -e -s -l -a"},c:[{cN:"meta",b:/^#![^\n]+sh\s*$/,r:10},{cN:"function",b:/\w[\w\d_]*\s*\(\s*\)\s*\{/,rB:!0,c:[e.inherit(e.TM,{b:/\w[\w\d_]*/})],r:0},e.HCM,s,a,t]}});hljs.registerLanguage("php",function(e){var c={b:"\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*"},i={cN:"meta",b:/<\?(php)?|\?>/},t={cN:"string",c:[e.BE,i],v:[{b:'b"',e:'"'},{b:"b'",e:"'"},e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null})]},a={v:[e.BNM,e.CNM]};return{aliases:["php3","php4","php5","php6"],cI:!0,k:"and include_once list abstract global private echo interface as static endswitch array null if endwhile or const for endforeach self var while isset public protected exit foreach throw elseif include __FILE__ empty require_once do xor return parent clone use __CLASS__ __LINE__ else break print eval new catch __METHOD__ case exception default die require __FUNCTION__ enddeclare final try switch continue endfor endif declare unset true false trait goto instanceof insteadof __DIR__ __NAMESPACE__ yield finally",c:[e.HCM,e.C("//","$",{c:[i]}),e.C("/\\*","\\*/",{c:[{cN:"doctag",b:"@[A-Za-z]+"}]}),e.C("__halt_compiler.+?;",!1,{eW:!0,k:"__halt_compiler",l:e.UIR}),{cN:"string",b:/<<<['"]?\w+['"]?$/,e:/^\w+;?$/,c:[e.BE,{cN:"subst",v:[{b:/\$\w+/},{b:/\{\$/,e:/\}/}]}]},i,{cN:"keyword",b:/\$this\b/},c,{b:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{cN:"function",bK:"function",e:/[;{]/,eE:!0,i:"\\$|\\[|%",c:[e.UTM,{cN:"params",b:"\\(",e:"\\)",c:["self",c,e.CBCM,t,a]}]},{cN:"class",bK:"class interface",e:"{",eE:!0,i:/[:\(\$"]/,c:[{bK:"extends implements"},e.UTM]},{bK:"namespace",e:";",i:/[\.']/,c:[e.UTM]},{bK:"use",e:";",c:[e.UTM]},{b:"=>"},t,a]}});hljs.registerLanguage("ruby",function(e){var b="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",r={keyword:"and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor",literal:"true false nil"},c={cN:"doctag",b:"@[A-Za-z]+"},a={b:"#<",e:">"},s=[e.C("#","$",{c:[c]}),e.C("^\\=begin","^\\=end",{c:[c],r:10}),e.C("^__END__","\\n$")],n={cN:"subst",b:"#\\{",e:"}",k:r},t={cN:"string",c:[e.BE,n],v:[{b:/'/,e:/'/},{b:/"/,e:/"/},{b:/`/,e:/`/},{b:"%[qQwWx]?\\(",e:"\\)"},{b:"%[qQwWx]?\\[",e:"\\]"},{b:"%[qQwWx]?{",e:"}"},{b:"%[qQwWx]?<",e:">"},{b:"%[qQwWx]?/",e:"/"},{b:"%[qQwWx]?%",e:"%"},{b:"%[qQwWx]?-",e:"-"},{b:"%[qQwWx]?\\|",e:"\\|"},{b:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/},{b:/<<(-?)\w+$/,e:/^\s*\w+$/}]},i={cN:"params",b:"\\(",e:"\\)",endsParent:!0,k:r},d=[t,a,{cN:"class",bK:"class module",e:"$|;",i:/=/,c:[e.inherit(e.TM,{b:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?"}),{b:"<\\s*",c:[{b:"("+e.IR+"::)?"+e.IR}]}].concat(s)},{cN:"function",bK:"def",e:"$|;",c:[e.inherit(e.TM,{b:b}),i].concat(s)},{b:e.IR+"::"},{cN:"symbol",b:e.UIR+"(\\!|\\?)?:",r:0},{cN:"symbol",b:":(?!\\s)",c:[t,{b:b}],r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{cN:"params",b:/\|/,e:/\|/,k:r},{b:"("+e.RSR+"|unless)\\s*",k:"unless",c:[a,{cN:"regexp",c:[e.BE,n],i:/\n/,v:[{b:"/",e:"/[a-z]*"},{b:"%r{",e:"}[a-z]*"},{b:"%r\\(",e:"\\)[a-z]*"},{b:"%r!",e:"![a-z]*"},{b:"%r\\[",e:"\\][a-z]*"}]}].concat(s),r:0}].concat(s);n.c=d,i.c=d;var l="[>?]>",o="[\\w#]+\\(\\w+\\):\\d+:\\d+>",u="(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>",w=[{b:/^\s*=>/,starts:{e:"$",c:d}},{cN:"meta",b:"^("+l+"|"+o+"|"+u+")",starts:{e:"$",c:d}}];return{aliases:["rb","gemspec","podspec","thor","irb"],k:r,i:/\/\*/,c:s.concat(w).concat(d)}});hljs.registerLanguage("http",function(e){var t="HTTP/[0-9\\.]+";return{aliases:["https"],i:"\\S",c:[{b:"^"+t,e:"$",c:[{cN:"number",b:"\\b\\d{3}\\b"}]},{b:"^[A-Z]+ (.*?) "+t+"$",rB:!0,e:"$",c:[{cN:"string",b:" ",e:" ",eB:!0,eE:!0},{b:t},{cN:"keyword",b:"[A-Z]+"}]},{cN:"attribute",b:"^\\w",e:": ",eE:!0,i:"\\n|\\s|=",starts:{e:"$",r:0}},{b:"\\n\\n",starts:{sL:[],eW:!0}}]}});hljs.registerLanguage("cs",function(e){var i={keyword:"abstract as base bool break byte case catch char checked const continue decimal default delegate do double enum event explicit extern finally fixed float for foreach goto if implicit in int interface internal is lock long nameof object operator out override params private protected public readonly ref sbyte sealed short sizeof stackalloc static string struct switch this try typeof uint ulong unchecked unsafe ushort using virtual void volatile while add alias ascending async await by descending dynamic equals from get global group into join let on orderby partial remove select set value var where yield",literal:"null false true"},t={cN:"string",b:'@"',e:'"',c:[{b:'""'}]},r=e.inherit(t,{i:/\n/}),a={cN:"subst",b:"{",e:"}",k:i},c=e.inherit(a,{i:/\n/}),n={cN:"string",b:/\$"/,e:'"',i:/\n/,c:[{b:"{{"},{b:"}}"},e.BE,c]},s={cN:"string",b:/\$@"/,e:'"',c:[{b:"{{"},{b:"}}"},{b:'""'},a]},o=e.inherit(s,{i:/\n/,c:[{b:"{{"},{b:"}}"},{b:'""'},c]});a.c=[s,n,t,e.ASM,e.QSM,e.CNM,e.CBCM],c.c=[o,n,r,e.ASM,e.QSM,e.CNM,e.inherit(e.CBCM,{i:/\n/})];var l={v:[s,n,t,e.ASM,e.QSM]},b=e.IR+"(<"+e.IR+"(\\s*,\\s*"+e.IR+")*>)?(\\[\\])?";return{aliases:["csharp"],k:i,i:/::/,c:[e.C("///","$",{rB:!0,c:[{cN:"doctag",v:[{b:"///",r:0},{b:""},{b:""}]}]}),e.CLCM,e.CBCM,{cN:"meta",b:"#",e:"$",k:{"meta-keyword":"if else elif endif define undef warning error line region endregion pragma checksum"}},l,e.CNM,{bK:"class interface",e:/[{;=]/,i:/[^\s:]/,c:[e.TM,e.CLCM,e.CBCM]},{bK:"namespace",e:/[{;=]/,i:/[^\s:]/,c:[e.inherit(e.TM,{b:"[a-zA-Z](\\.?\\w)*"}),e.CLCM,e.CBCM]},{cN:"meta",b:"^\\s*\\[",eB:!0,e:"\\]",eE:!0,c:[{cN:"meta-string",b:/"/,e:/"/}]},{bK:"new return throw await else",r:0},{cN:"function",b:"("+b+"\\s+)+"+e.IR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:i,c:[{b:e.IR+"\\s*\\(",rB:!0,c:[e.TM],r:0},{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,k:i,r:0,c:[l,e.CNM,e.CBCM]},e.CLCM,e.CBCM]}]}});hljs.registerLanguage("shell",function(s){return{aliases:["console"],c:[{cN:"meta",b:"^\\s{0,3}[\\w\\d\\[\\]()@-]*[>%$#]",starts:{e:"$",sL:"bash"}}]}});hljs.registerLanguage("objectivec",function(e){var t={cN:"built_in",b:"\\b(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)\\w+"},_={keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required @encode @package @import @defs @compatibility_alias __bridge __bridge_transfer __bridge_retained __bridge_retain __covariant __contravariant __kindof _Nonnull _Nullable _Null_unspecified __FUNCTION__ __PRETTY_FUNCTION__ __attribute__ getter setter retain unsafe_unretained nonnull nullable null_unspecified null_resettable class instancetype NS_DESIGNATED_INITIALIZER NS_UNAVAILABLE NS_REQUIRES_SUPER NS_RETURNS_INNER_POINTER NS_INLINE NS_AVAILABLE NS_DEPRECATED NS_ENUM NS_OPTIONS NS_SWIFT_UNAVAILABLE NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_REFINED_FOR_SWIFT NS_SWIFT_NAME NS_SWIFT_NOTHROW NS_DURING NS_HANDLER NS_ENDHANDLER NS_VALUERETURN NS_VOIDRETURN",literal:"false true FALSE TRUE nil YES NO NULL",built_in:"BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once"},i=/[a-zA-Z@][a-zA-Z0-9_]*/,n="@interface @class @protocol @implementation";return{aliases:["mm","objc","obj-c"],k:_,l:i,i:""}]}]},{cN:"class",b:"("+n.split(" ").join("|")+")\\b",e:"({|$)",eE:!0,k:n,l:i,c:[e.UTM]},{b:"\\."+e.UIR,r:0}]}});hljs.registerLanguage("makefile",function(e){var i={cN:"variable",v:[{b:"\\$\\("+e.UIR+"\\)",c:[e.BE]},{b:/\$[@% b.score){return 1;} return 0; } function editDistance(s1, s2) { const m = s1.length; const n = s2.length; const dTable = []; for (let i = 0; i < m+1; i += 1) { dTable.push(Array(n+i).fill(0)) } for (let i = 0; i < m+1; i += 1) { for (let j = 0; j < n+1; j += 1) { if ( i == 0) {dTable[i][j] = j} else if (j == 0) {dTable[i][j] = i} else if (s1[i-1] == s2[j-1]) { dTable[i][j] = dTable[i-1][j-1]; } else { dTable[i][j] = 1 + Math.min( dTable[i][j-1], dTable[i-1][j], dTable[i-1][j-1] ) } } } return dTable[m][n] } function search(query, idx) { const matches = []; for (let i = 0; i < idx.length; i += 1) { // is identical if (query == idx[i].normalized_name) { idx[i].score = idx[i].weight * 10**6; matches.push(idx[i]); } // is substring else if (idx[i].normalized_name.indexOf(query) !== -1) { idx[i].score = idx[i].weight * 10**5; matches.push(idx[i]); } // edit distance of 1 else { // TODO: tokenize idx[i] // tokens = query.split(" "); if (matches.length < 5) { if (editDistance(query, idx[i].normalized_name) < 3){ idx[i].score = idx[i].weight * 100; matches.push(idx[i]); } else if (editDistance(query, idx[i].normalized_name) < 4){ idx[i].score = idx[i].weight * 50; matches.push(idx[i]); } } } } return matches.sort(compare).reverse().slice(0, 5); } function inArray(obj, arr){ let found = false; for (let i = 0; i < arr.length; i += 1) { if (arr[i].normalized_name == obj.normalized_name) { found = true; break; } } return found; } /* * Gets list of results by category and updates the dropdown */ function createList(parent, arr, name) { if (arr){ if (arr.length > 0) { /*for each item in the results...*/ header = document.createElement("DIV"); header.setAttribute("class", "disabled"); header.innerHTML = `
${name}
`; parent.appendChild(header); for (i = 0; i < arr.length; i++) { /*create a DIV element for each matching element:*/ b = document.createElement("DIV"); b.setAttribute("href", arr[i].url); b.innerHTML = `${arr[i].name} ` /*insert a input field that will hold the current array item's value:*/ b.innerHTML += ""; parent.appendChild(b); } } } } /* * Adapted from https://www.w3schools.com/howto/howto_js_autocomplete.asp */ function autocomplete(inp) { /*the autocomplete function takes two arguments, the text field element and an array of possible autocompleted values:*/ let currentFocus; /*execute a function when someone writes in the text field:*/ inp.addEventListener("input", function (e) { let a, b, i, val = this.value; /*close any already open lists of autocompleted values*/ closeAllLists(); if (!val) { return false; } currentFocus = -1; /*create a DIV element that will contain the items (values):*/ a = document.createElement("DIV"); a.setAttribute("id", this.id + "-autocomplete-list"); a.setAttribute("class", "autocomplete-items"); /*append the DIV element as a child of the autocomplete container:*/ this.parentNode.appendChild(a); // RESULTS for query query = val.toLowerCase().trim(); // getting results let sites = search(query, sites_idx); let trackers = search(query, trackers_idx); let blogposts = search(query, blog_idx); // Update UI createList(a, sites, "websites"); createList(a, trackers, "trackers"); createList(a, blogposts, "blogposts"); }); /*execute a function presses a key on the keyboard:*/ inp.addEventListener("keydown", function (e) { let x = document.getElementById(this.id + "-autocomplete-list"); if (x) { x = x.getElementsByTagName("div"); } if (e.keyCode == 40) { /*If the arrow DOWN key is pressed, increase the currentFocus variable:*/ if (currentFocus + 1 < x.length) { if (x[currentFocus + 1].classList.contains('disabled')) { currentFocus += 2; } else { currentFocus += 1; } } else { currentFocus = 1; } addActive(x); } else if (e.keyCode == 38) { //up /*If the arrow UP key is pressed, decrease the currentFocus variable:*/ if (x[currentFocus - 1].classList.contains('disabled')) { currentFocus -= 2; } else { currentFocus -= 1; } /*and and make the current item more visible:*/ addActive(x); } else if (e.keyCode == 13 & currentFocus) { if (!x[currentFocus].classList.contains('disabled')) { /*If the ENTER key is pressed, prevent the form from being submitted,*/ e.preventDefault(); if (currentFocus > -1) { /*and simulate a click on the "active" item:*/ // if (x) x[currentFocus].click(); window.location.href = pathToRoot + x[currentFocus].getAttribute("href"); } } } }); function addActive(x) { /*a function to classify an item as "active":*/ if (!x) return false; /*start by removing the "active" class on all items:*/ removeActive(x); if (currentFocus >= x.length) currentFocus = 0; if (currentFocus < 0) currentFocus = (x.length - 1); /*add class "autocomplete-active":*/ x[currentFocus].classList.add("autocomplete-active"); } function removeActive(x) { /*a function to remove the "active" class from all autocomplete items:*/ for (let i = 0; i < x.length; i++) { x[i].classList.remove("autocomplete-active"); } } function closeAllLists(elmnt) { /*close all autocomplete lists in the document, except the one passed as an argument:*/ let x = document.getElementsByClassName("autocomplete-items"); for (let i = 0; i < x.length; i++) { if (elmnt != x[i] && elmnt != inp) { x[i].parentNode.removeChild(x[i]); } } } /*execute a function when someone clicks in the document:*/ document.addEventListener("click", function (e) { closeAllLists(e.target); }); } ================================================ FILE: static/scss/_colors.scss ================================================ $grey-txt-color: #71869E; $red-txt-color: #A069AB; $light-blue-bg: #F4F7F8; $header-bg-color: #253135; $link-color: #0078CA; $dark-header: #48374E; $blue-color: #00AEF0; $blue-on-dark: #88DEFF; $light-blue-gray: #e3eced; $dark-purple: #8B4099; $nav-bar-active: #2b599c; $dark-blue: #2B5993; $light-purple-border: #f4e0ee; $black-color: #1A1925; $inactive-gray: #e9e9e9; $graph-gray: #BCC4CE; $pressed-button-dark: #3b2e40; $button-border-dark: #36263c; ================================================ FILE: static/scss/blog/card.scss ================================================ @import '../colors'; $font-serif: Palatino, "Palatino Linotype", "Palatino LT STD", "Book Antiqua", Georgia, serif; .col-md-4{ margin-bottom: 40px; } .card { border-radius: 4px; h3 { font-size: 19px; padding-bottom: 10px; margin-top: 5px; a { color: $black-color; font-weight: bold; } } p { font-family: $font-serif; color: $grey-txt-color; font-size: 18px; margin-top: -10px; } .thumbnail { padding: 0; box-shadow: 8px 14px 38px rgba(39,44,49,.06),1px 3px 8px rgba(39,44,49,.03); border: 0; } .card-media { padding: 0; height: 198px !important; width: 100% !important; display: flex; max-width: 100% !important; .img-fit-cover { border-top-left-radius: 4px; border-top-right-radius: 4px; object-fit: cover; max-width: inherit; } } .caption { padding: 30px; min-height: 200px; max-height: 200px; .primer{ font-family: 'FiraMono-Regular', monospace; font-size: 10px; text-transform: uppercase; letter-spacing: 1px; font-weight: bold; margin-top: -13px; color: $red-txt-color; } .article{ font-family: 'FiraMono-Regular', monospace; font-size: 10px; text-transform: uppercase; letter-spacing: 1px; font-weight: bold; margin-top: -13px; color: $grey-txt-color; } } } ================================================ FILE: static/scss/blog/github.scss ================================================ /* github.com style (c) Vasily Polovnyov */ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hljs-comment, .hljs-quote { color: #998; font-style: italic; } .hljs-keyword, .hljs-selector-tag, .hljs-subst { color: #333; font-weight: bold; } .hljs-number, .hljs-literal, .hljs-variable, .hljs-template-variable, .hljs-tag .hljs-attr { color: #008080; } .hljs-string, .hljs-doctag { color: #d14; } .hljs-title, .hljs-section, .hljs-selector-id { color: #900; font-weight: bold; } .hljs-subst { font-weight: normal; } .hljs-type, .hljs-class .hljs-title { color: #458; font-weight: bold; } .hljs-tag, .hljs-name, .hljs-attribute { color: #000080; font-weight: normal; } .hljs-regexp, .hljs-link { color: #009926; } .hljs-symbol, .hljs-bullet { color: #990073; } .hljs-built_in, .hljs-builtin-name { color: #0086b3; } .hljs-meta { color: #999; font-weight: bold; } .hljs-deletion { background: #fdd; } .hljs-addition { background: #dfd; } .hljs-emphasis { font-style: italic; } .hljs-strong { font-weight: bold; } ================================================ FILE: static/scss/blog/post.scss ================================================ @import '../colors'; $font-serif: Palatino, "Palatino Linotype", "Palatino LT STD", "Book Antiqua", Georgia, serif; h1, h2, h3, h4, h5 { font-family: $font-serif; margin-top: 42px; } .img-container{ text-align: center; margin-top: 32px; max-width: 700px; margin-left: auto; margin-right: auto; } .container-post{ font-family: $font-serif; color: black; font-size: 19px; max-width: 700px; padding-bottom: 126px; #post-title{ font-family: 'Open Sans', sans-serif; font-size: 30px; color: #1A1925; } .post-meta { margin-bottom: 35px; margin-top: -15px; } #post-subtitle{ margin-top: 0 !important; color: $grey-txt-color; line-height: 1.3; } h1{ font-size: 29px; font-weight: bold; } h2 { font-size: 25px; font-weight: bold; } h3 { font-size: 21px; font-weight: bold; } a{ color: $link-color; code { color: $link-color; font-weight: bold; border-bottom: solid 1px; } } a:hover{ color: #CF635B; text-decoration: none; border-bottom: solid 2px #CF635B; } p{ line-height: 1.7; font-size: 19px; margin-top: 19px; } li{ line-height: 1.7; } table { font-size: 16px; } dt { font-weight: 700; margin-top: 25px; } dd{ line-height: 1.7; margin-left: 50px; } .footnote { li { margin-bottom: -10px; } } img{ width: 100%; margin-bottom: 30px; margin-top: 30px; } .img-with-padding{ padding: 30px; border-radius: 2px; } .img-caption{ font-style: italic; font-size: 16px !important; color: #666; margin-top: -20px; margin-bottom: 30px; } blockquote { padding: 10px 20px; margin: 0 0 20px; border-left: 5px solid black; p { margin-top: 0px; } } } .img-responsive{ margin: auto; box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); } pre{ padding: 0; margin-top: 30px; margin-bottom: 30px; box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); border: none; code { white-space: inherit; } } code{ padding: 2px 4px; font-size: 85%; font-family: 'FiraMono-Regular', monospace; color: #222; background-color: #f8f8f8; border-radius: 4px; } ================================================ FILE: static/scss/bootstrap.min.scss ================================================ /*! * Bootstrap v3.3.7 (http://getbootstrap.com) * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} /*# sourceMappingURL=bootstrap.min.css.map */ ================================================ FILE: static/scss/companies/reach-chart.scss ================================================ @import '../colors'; .home-plot { color: $grey-txt-color; h4 { font-size: 11px; color: $grey-txt-color; margin-top: 25px; } a { color: $grey-txt-color; } .plot-subtitle { margin-bottom: 30px; } .plot-title { font-size: 23px; font-weight: bold; text-transform: uppercase; color: $black-color; } .checkout { i { vertical-align: top; margin-right: 5px; padding-top: 4px; } span { display: inline-block; max-width: 170px; } } } .svg-container { border-radius: 4px; box-shadow: 8px 14px 38px rgba(39,44,49,0.06), 1px 3px 8px rgba(39,44,49,0.03); } .plot-caption{ font-size: 14px; color: $grey-txt-color; margin-top: 10px; } ================================================ FILE: static/scss/custom.scss ================================================ @import 'colors'; html { height: 100%; box-sizing: border-box; } @font-face { font-family: "Open Sans"; src: url('../fonts/OpenSans-Regular.ttf'); } @font-face { font-family: "Open Sans Semibold"; src: url('../fonts/OpenSans-Semibold.ttf'); } @font-face { font-family: "Open Sans Bold"; src: url('../fonts/OpenSans-bold.ttf'); } @font-face { font-family: "FiraMono-Regular"; src: url('../fonts/FiraMono-Regular.ttf'); } @font-face { font-family: 'FiraMono-Bold'; src: url('../fonts/FiraMono-Bold.ttf'); } body{ position: relative; margin: 0; min-height: 100%; font-family: 'Open Sans', sans-serif; } .breadcrumb-bar{ margin-top: -1px; height: 39px; background-color: transparent; position: relative; z-index: 2; .breadcrumb{ color: $grey-txt-color; background-color: transparent; padding-left: 0; overflow: hidden; white-space: nowrap; margin: 0; li + li:before { font-family: "FontAwesome"; color: $grey-txt-color; content: "\f105 "; padding: 0 15px; } a{ font-family: "Open Sans"; color: inherit; } } } h2, h3 { font-size: 23px; } p { font-size: 16px; } .top-buffer { padding-top:40px; } .bottom-buffer { margin-bottom: 20px; } .red-highlight { color: $red-txt-color; } .btn-blue { background-color: $blue-color; color: #fff; font-size: 16px; } .btn-purple { background-color: $dark-purple; color: #fff; } .grey-bg { background-color: #F4F7F8; border-bottom: 1px solid aliceblue; padding-bottom: 20px; border-bottom: solid 1px $light-blue-gray; } .percentage { color: $red-txt-color; font-size: 45px; font-family: 'FiraMono-Regular', monospace; } .purple-color { color: $red-txt-color; } .cat-item { border: 1px solid $light-blue-gray; border-radius: 20px; padding: 2px 6px 2px; margin-bottom: 2px; display: inline-block; font-size: 13px; color: $grey-txt-color; i { font-size: 12px; line-height: 12px; padding-right: 2px; } } .header { padding-top: 20px; } .truncate { text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } .label-category { background-color: #fff; border: 1px solid #CFD8DB; border-radius: 12px; color: #5F7C86; font-weight: normal; } /* Navbar */ .navbar-brand { height: auto; } .navbar-brand img { height: 31px; } .navbar-blue { background-color: $blue-color; .navbar-nav a { color: #fff; } .navbar-toggle .icon-bar { background-color: #fff } } #search-bar { border-radius: 2px; border: none; background-color: $light-blue-bg; color: $black-color; margin-top: 13px; width: 400px !important; padding-left: 17px; } #search-bar:focus{ background-color: white; } /* AUTOCOMPLETE STYLING */ * { box-sizing: border-box; } .disabled{ pointer-events: none; color: $dark-purple; } .disabled:hover{ pointer-events: none; color: $dark-purple; } .autocomplete { /*the container must be positioned relative:*/ position: relative; display: inline-block; } .autocomplete-items { background: white; position: absolute; z-index: 99; top: 100%; padding: 2px; margin-top: 15px; border-radius: 2px; left: 0px; right: 0; // min-width: 100vh; box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.15); div { padding-top: 5px; padding-bottom: 5px; padding-left: 15px; padding-right: 15px; cursor: pointer; background-color: #fff; i{ float: right; color: #e7e7e7; margin-left: -10px; margin-top: 4px; } a { display: block; color: $black-color; } a:hover { text-decoration: none; } a:focus{ color: white; } } .autocomplete-active{ /*when navigating through the items using the arrow keys:*/ background-color: DodgerBlue !important; color: white; a { color: white; } } div:hover{ background-color: #e9e9e9; i { color: $black-color !important; } } } #imaginary_container { float: left; } .navbar { margin-bottom: 0; border-radius: 0; position: relative; z-index: 10; box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.15); } .navbar-nav a { text-transform: uppercase; font-size: 13px; letter-spacing: 2px; } .navbar-nav .active a { color: $nav-bar-active; } .navbar-nav > li > a { padding-top: 21px; font-weight: 600; &:hover, &:active, &:focus { color: $nav-bar-active; background-color: transparent; } } .privacy-policy { margin-top: 100px; margin-bottom: 150px; max-width: 800px; li { list-style: none; } } .imprint { margin-bottom: 150px; .w-container { margin-left: auto; margin-right: auto; max-width: 940px; } .w-container:before, .w-container:after { content: " "; display: table; grid-column-start: 1; grid-row-start: 1; grid-column-end: 2; grid-row-end: 2; } .w-container:after { clear: both; } .w-container .w-row { margin-left: -10px; margin-right: -10px; } .w-row:before, .w-row:after { content: " "; display: table; grid-column-start: 1; grid-row-start: 1; grid-column-end: 2; grid-row-end: 2; } .basic-container { text-align: left; } .basic-container._50l { padding-top: 90px; padding-right: 470px; } .ghostery_spacer-40 { height: 25px; } } footer { background: $light-blue-bg; padding-top: 42px; padding-bottom: 42px; color: $grey-txt-color; z-index: 3; position: relative; a { color: $link-color; } .menu { .margin-left { margin-left: 32px; } ul { padding-left: 0px !important; li { list-style: none; margin-bottom: 15px; font-size: 14px; i { min-width: 25px; } a { color: $grey-txt-color; } } } } .logos { a { text-decoration: none; } span { display: inline-block; } .copyright { margin-top: 15px; } .cliqz-logo { background-image: url('../img/cliqz_logo_color.png'); width: 100px; height: 36px; margin-right: 35px; } .ghostery-logo { background-image: url('../img/ghostery_logo color.png'); width: 131px; height: 39px; } } } .dark-page { background-color: $dark-header; margin-top: -42px; padding-top: 42px; } #multi-column-list { padding-left: 0; margin-top: 20px; margin-bottom: 50px; column-count: 3; -webkit-column-count: 3; -moz-column-count: 3; li { display: inline-block; padding-bottom: 10px; width: 100%; color: $grey-txt-color; a { color: $red-txt-color; font-size: 16px; } span { color: $grey-txt-color; margin-left: 5px; } } } .nav-tabs { border-bottom: 0; & > li { margin-right: 5px; & > a { border-radius: 6px; color: #666; border: 1px solid #ccc; font-size: 13px; padding: 3px 7px; &:hover { border-color: #ccc; } } &.active > a, &.active > a:focus { border-bottom-color: #ccc; background-color: #f0f0f0; &:hover { border-bottom-color: #ccc; background-color: #eee; } } } } .inverse { .nav-tabs { margin-bottom: 20px; & > li { & > a { color: #fff; &:hover, a:focus { background: #4f5f64; } } &.active > a, &.active > a:focus { background-color: #41555c; border: 1px solid #41555c; color: #fff; } } } } /*COMMON WEBSITES & TRACKERS */ #overview-header { background-color: $light-blue-bg; margin-top: -40px; padding-top: 47px; border-bottom: solid 1px #e3eced; } .overview-info { color: $grey-txt-color; margin-bottom: 5px; h1 { font-size: 30px; font-weight: 500; a { color: $black-color; text-decoration: none; } } .icon { position: relative; top: -1px; } .subtitle { font-size: 16px; } .overview-info-right { margin-top: 30px; .trackers-info { border: 3px solid $red-txt-color; padding: 5px; .trackers-icon { float: left; } .header { font-size: 16px; color: $red-txt-color; font-family: 'FiraMono-Bold', monospace; } .subtitle { font-size: 13px; } p { overflow: hidden; margin-left: 5px; margin-bottom: 0; } } .more-info { margin-top: 10px; font-size: 13px; p { margin-bottom: 4px; } } } &.inverse { color: #fff; p { margin-bottom: 2px; } a { color: $blue-on-dark; } .header { color: rgba(255, 255, 255, 0.7); font-size: 13px; } } } .overview-header-box { background: #fff; margin-bottom: 40px; box-shadow: 8px 14px 38px rgba(39,44,49,.06),1px 3px 8px rgba(39,44,49,.03);; border-radius: 4px; padding: 20px 0 30px 25px; h3 { font-size: 13px; color: $grey-txt-color; } .percentage { font-size: 30px; color: $red-txt-color; font-family: 'FiraMono-Regular', monospace; } .tracking-methods { img { position: absolute; left: -12px; width: 30px; } p { font-size: 13px; } /* p, a, h3 { margin-left: 30px; }*/ a { color: $grey-txt-color; } } } .tracking-method { .highlight { color: $red-txt-color; } } .clearfix:after { clear: both; content: " "; display: block; font-size: 0; height: 0; visibility: hidden; } .blog-page { .col:nth-child(4n) {clear: left;} .header { margin-bottom: 30px; } .btn-purple { border-radius: 0; box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.11); padding: 12px; margin-top: 10px; } a:hover { text-decoration: none; } } .overview-page { .tracking-methods { img { width: 20px; } } } .navbar-toggle { margin-right: 0; } @media only screen and (max-width : 1200px) { .header-articles:before { height: 161px !important; } #search-bar-autocomplete-list { width: 100%vh !important; } } @media only screen and (max-width : 992px) { .navbar-header { float: none; } .navbar-left,.navbar-right { float: none !important; } .navbar-toggle { display: block; } .navbar-collapse { border-top: 1px solid transparent; box-shadow: inset 0 1px 0 rgba(255,255,255,0.1); } .navbar-fixed-top { top: 0; border-width: 0 0 1px; } .navbar-collapse.collapse { display: none!important; } .navbar-nav { float: none!important; margin-top: 7.5px; } .navbar-nav>li { float: none; } .navbar-nav>li>a { padding-top: 10px; padding-bottom: 10px; } .collapse.in{ display:block !important; } #myNavbar { margin-left: -30px; } .home-header { padding-top: 30px !important; h1 { font-size: 23px !important; } } .header-articles:before { height: 120px !important; } .header-articles .caption { padding: 15px !important; } #multi-column-list { column-count: 2; -webkit-column-count: 2; -moz-column-count: 2; } .blog-page { .col:nth-child(3n) {clear: left;} } #search-bar, #search-bar-autocomplete-list, #imaginary_container .input-group, #eac-container-search-bar { width: 100% !important; } #imaginary_container { padding-left: 14px; float: none; } } /* Small Devices, Tablets */ @media only screen and (max-width : 768px) { #imaginary_container { height: 57px; } .navbar-collapse.in { overflow-y: visible; } #myNavbar { margin-left: 0; } #imaginary_container { margin-left: -14px; } .input-group { display: block; } #search-bar, #search-bar-autocomplete-list{ width: 100% !important; } .autocomplete-items{ margin-top: 50px; } .home-header h1 { max-width: 600px !important; } .trackers-img img { display: none; } .header-articles { margin-top: 20px; } .header-articles:before { height: 0px !important; } #multi-column-list { column-count: 1; -webkit-column-count: 1; -moz-column-count: 1; } .blog-page .btn-blue { float: none; text-align: center; width: 100%; } } .navbar-ghostery { padding: 16px 20px; background:#202225; color: white; font-family: 'Open Sans'; } .navbar-ghostery-header { display: flex; justify-content: space-between; align-items: center; gap: 8px; } .navbar-ghostery-header img { flex: 0 1 auto; min-width: 0; } .navbar-ghostery-header button { display: flex; flex-direction: row; align-items: center; padding: 6px 12px; gap: 4px; border: 1px solid #636468; border-radius: 24px; background: none; font-family: inherit; flex-shrink: 0; color: white; text-decoration: none; font-family: Open Sans Semibold; font-size: 13px; line-height: 18px; } .navbar-ghostery-header button span { display: none; } .navbar-ghostery.open .navbar-ghostery-hide, .navbar-ghostery:not(.open) .navbar-ghostery-show { display: inline; } .navbar-ghostery-features { display: flex; flex-flow: column; gap: 16px; overflow: hidden; box-sizing: border-box; transition: max-height 0.5s ease-in-out; max-height: 0; } .navbar-ghostery.open .navbar-ghostery-features { max-height: 1000px; } .navbar-ghostery-features div:first-child { margin-top: 20px; } .navbar-ghostery-features div { display: flex; align-items: center; gap: 16px; background: #2F3136; border: 1px solid #3F4146; box-shadow: 30px 60px 80px rgba(0, 0, 0, 0.15); border-radius: 16px; padding: 16px; width: 100%; } .navbar-ghostery-features div section { display: flex; flex-direction: column; gap: 8px; flex: 1 1 auto; } .navbar-ghostery-features h3 { font-family: Open Sans Semibold; font-size: 18px; line-height: 22px; margin: 0; padding: 0; } .navbar-ghostery-features p { font-family: Open Sans; font-size: 12px; line-height: 18px; margin: 0; padding: 0; } .navbar-ghostery-features a { color: inherit; text-decoration: underline; } @media screen and (min-width: 768px) { .navbar-ghostery { padding: 16px 40px; } .navbar-ghostery-header img { height: 32px; } .navbar-ghostery-header button { padding: 6px 16px; line-height: 20px; } .navbar-ghostery-features div:first-child { margin-top: 24px; } .navbar-ghostery-features img { width: 160px; height: 160px; } .navbar-ghostery-features p { font-size: 13px; line-height: 20px; } } @media screen and (min-width: 992px) { .navbar-ghostery > * { max-width: 1140px; margin: 0 auto; } .navbar-ghostery-features { flex-flow: row; } .navbar-ghostery-features div { flex-direction: column; align-items: flex-start; margin-top: 24px; width: 25%; } } ================================================ FILE: static/scss/datatables.colReorder.min.scss ================================================ table.DTCR_clonedTable.dataTable{position:absolute !important;background-color:rgba(255,255,255,0.7);z-index:202}div.DTCR_pointer{width:1px;background-color:#337ab7;z-index:201} ================================================ FILE: static/scss/datatables.min.scss ================================================ table.dataTable{width:100%;margin:0 auto;clear:both;border-collapse:separate;border-spacing:0}table.dataTable thead th,table.dataTable tfoot th{font-weight:bold}table.dataTable thead th,table.dataTable thead td{padding:10px 18px;border-bottom:1px solid #111}table.dataTable thead th:active,table.dataTable thead td:active{outline:none}table.dataTable tfoot th,table.dataTable tfoot td{padding:10px 18px 6px 18px;border-top:1px solid #111}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_desc,table.dataTable thead .sorting_asc_disabled,table.dataTable thead .sorting_desc_disabled{cursor:pointer;*cursor:hand;background-repeat:no-repeat;background-position:center right}table.dataTable thead .sorting{background-image:url("../images/sort_both.png")}table.dataTable thead .sorting_asc{background-image:url("../images/sort_asc.png")}table.dataTable thead .sorting_desc{background-image:url("../images/sort_desc.png")}table.dataTable thead .sorting_asc_disabled{background-image:url("../images/sort_asc_disabled.png")}table.dataTable thead .sorting_desc_disabled{background-image:url("../images/sort_desc_disabled.png")}table.dataTable tbody tr{background-color:#ffffff}table.dataTable tbody tr.selected{background-color:#B0BED9}table.dataTable tbody th,table.dataTable tbody td{padding:8px 10px}table.dataTable.row-border tbody th,table.dataTable.row-border tbody td,table.dataTable.display tbody th,table.dataTable.display tbody td{border-top:1px solid #ddd}table.dataTable.row-border tbody tr:first-child th,table.dataTable.row-border tbody tr:first-child td,table.dataTable.display tbody tr:first-child th,table.dataTable.display tbody tr:first-child td{border-top:none}table.dataTable.cell-border tbody th,table.dataTable.cell-border tbody td{border-top:1px solid #ddd;border-right:1px solid #ddd}table.dataTable.cell-border tbody tr th:first-child,table.dataTable.cell-border tbody tr td:first-child{border-left:1px solid #ddd}table.dataTable.cell-border tbody tr:first-child th,table.dataTable.cell-border tbody tr:first-child td{border-top:none}table.dataTable.stripe tbody tr.odd,table.dataTable.display tbody tr.odd{background-color:#f9f9f9}table.dataTable.stripe tbody tr.odd.selected,table.dataTable.display tbody tr.odd.selected{background-color:#acbad4}table.dataTable.hover tbody tr:hover,table.dataTable.display tbody tr:hover{background-color:#f6f6f6}table.dataTable.hover tbody tr:hover.selected,table.dataTable.display tbody tr:hover.selected{background-color:#aab7d1}table.dataTable.order-column tbody tr>.sorting_1,table.dataTable.order-column tbody tr>.sorting_2,table.dataTable.order-column tbody tr>.sorting_3,table.dataTable.display tbody tr>.sorting_1,table.dataTable.display tbody tr>.sorting_2,table.dataTable.display tbody tr>.sorting_3{background-color:#fafafa}table.dataTable.order-column tbody tr.selected>.sorting_1,table.dataTable.order-column tbody tr.selected>.sorting_2,table.dataTable.order-column tbody tr.selected>.sorting_3,table.dataTable.display tbody tr.selected>.sorting_1,table.dataTable.display tbody tr.selected>.sorting_2,table.dataTable.display tbody tr.selected>.sorting_3{background-color:#acbad5}table.dataTable.display tbody tr.odd>.sorting_1,table.dataTable.order-column.stripe tbody tr.odd>.sorting_1{background-color:#f1f1f1}table.dataTable.display tbody tr.odd>.sorting_2,table.dataTable.order-column.stripe tbody tr.odd>.sorting_2{background-color:#f3f3f3}table.dataTable.display tbody tr.odd>.sorting_3,table.dataTable.order-column.stripe tbody tr.odd>.sorting_3{background-color:whitesmoke}table.dataTable.display tbody tr.odd.selected>.sorting_1,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_1{background-color:#a6b4cd}table.dataTable.display tbody tr.odd.selected>.sorting_2,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_2{background-color:#a8b5cf}table.dataTable.display tbody tr.odd.selected>.sorting_3,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_3{background-color:#a9b7d1}table.dataTable.display tbody tr.even>.sorting_1,table.dataTable.order-column.stripe tbody tr.even>.sorting_1{background-color:#fafafa}table.dataTable.display tbody tr.even>.sorting_2,table.dataTable.order-column.stripe tbody tr.even>.sorting_2{background-color:#fcfcfc}table.dataTable.display tbody tr.even>.sorting_3,table.dataTable.order-column.stripe tbody tr.even>.sorting_3{background-color:#fefefe}table.dataTable.display tbody tr.even.selected>.sorting_1,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_1{background-color:#acbad5}table.dataTable.display tbody tr.even.selected>.sorting_2,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_2{background-color:#aebcd6}table.dataTable.display tbody tr.even.selected>.sorting_3,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_3{background-color:#afbdd8}table.dataTable.display tbody tr:hover>.sorting_1,table.dataTable.order-column.hover tbody tr:hover>.sorting_1{background-color:#eaeaea}table.dataTable.display tbody tr:hover>.sorting_2,table.dataTable.order-column.hover tbody tr:hover>.sorting_2{background-color:#ececec}table.dataTable.display tbody tr:hover>.sorting_3,table.dataTable.order-column.hover tbody tr:hover>.sorting_3{background-color:#efefef}table.dataTable.display tbody tr:hover.selected>.sorting_1,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_1{background-color:#a2aec7}table.dataTable.display tbody tr:hover.selected>.sorting_2,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_2{background-color:#a3b0c9}table.dataTable.display tbody tr:hover.selected>.sorting_3,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_3{background-color:#a5b2cb}table.dataTable.no-footer{border-bottom:1px solid #111}table.dataTable.nowrap th,table.dataTable.nowrap td{white-space:nowrap}table.dataTable.compact thead th,table.dataTable.compact thead td{padding:4px 17px 4px 4px}table.dataTable.compact tfoot th,table.dataTable.compact tfoot td{padding:4px}table.dataTable.compact tbody th,table.dataTable.compact tbody td{padding:4px}table.dataTable th.dt-left,table.dataTable td.dt-left{text-align:left}table.dataTable th.dt-center,table.dataTable td.dt-center,table.dataTable td.dataTables_empty{text-align:center}table.dataTable th.dt-right,table.dataTable td.dt-right{text-align:right}table.dataTable th.dt-justify,table.dataTable td.dt-justify{text-align:justify}table.dataTable th.dt-nowrap,table.dataTable td.dt-nowrap{white-space:nowrap}table.dataTable thead th.dt-head-left,table.dataTable thead td.dt-head-left,table.dataTable tfoot th.dt-head-left,table.dataTable tfoot td.dt-head-left{text-align:left}table.dataTable thead th.dt-head-center,table.dataTable thead td.dt-head-center,table.dataTable tfoot th.dt-head-center,table.dataTable tfoot td.dt-head-center{text-align:center}table.dataTable thead th.dt-head-right,table.dataTable thead td.dt-head-right,table.dataTable tfoot th.dt-head-right,table.dataTable tfoot td.dt-head-right{text-align:right}table.dataTable thead th.dt-head-justify,table.dataTable thead td.dt-head-justify,table.dataTable tfoot th.dt-head-justify,table.dataTable tfoot td.dt-head-justify{text-align:justify}table.dataTable thead th.dt-head-nowrap,table.dataTable thead td.dt-head-nowrap,table.dataTable tfoot th.dt-head-nowrap,table.dataTable tfoot td.dt-head-nowrap{white-space:nowrap}table.dataTable tbody th.dt-body-left,table.dataTable tbody td.dt-body-left{text-align:left}table.dataTable tbody th.dt-body-center,table.dataTable tbody td.dt-body-center{text-align:center}table.dataTable tbody th.dt-body-right,table.dataTable tbody td.dt-body-right{text-align:right}table.dataTable tbody th.dt-body-justify,table.dataTable tbody td.dt-body-justify{text-align:justify}table.dataTable tbody th.dt-body-nowrap,table.dataTable tbody td.dt-body-nowrap{white-space:nowrap}table.dataTable,table.dataTable th,table.dataTable td{box-sizing:content-box}.dataTables_wrapper{position:relative;clear:both;*zoom:1;zoom:1}.dataTables_wrapper .dataTables_length{float:left}.dataTables_wrapper .dataTables_filter{float:right;text-align:right}.dataTables_wrapper .dataTables_filter input{margin-left:0.5em}.dataTables_wrapper .dataTables_info{clear:both;float:left;padding-top:0.755em}.dataTables_wrapper .dataTables_paginate{float:right;text-align:right;padding-top:0.25em}.dataTables_wrapper .dataTables_paginate .paginate_button{box-sizing:border-box;display:inline-block;min-width:1.5em;padding:0.5em 1em;margin-left:2px;text-align:center;text-decoration:none !important;cursor:pointer;*cursor:hand;color:#333 !important;border:1px solid transparent;border-radius:2px}.dataTables_wrapper .dataTables_paginate .paginate_button.current,.dataTables_wrapper .dataTables_paginate .paginate_button.current:hover{color:#333 !important;border:1px solid #979797;background-color:white;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #fff), color-stop(100%, #dcdcdc));background:-webkit-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:-moz-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:-ms-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:-o-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:linear-gradient(to bottom, #fff 0%, #dcdcdc 100%)}.dataTables_wrapper .dataTables_paginate .paginate_button.disabled,.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover,.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active{cursor:default;color:#666 !important;border:1px solid transparent;background:transparent;box-shadow:none}.dataTables_wrapper .dataTables_paginate .paginate_button:hover{color:white !important;border:1px solid #111;background-color:#585858;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111));background:-webkit-linear-gradient(top, #585858 0%, #111 100%);background:-moz-linear-gradient(top, #585858 0%, #111 100%);background:-ms-linear-gradient(top, #585858 0%, #111 100%);background:-o-linear-gradient(top, #585858 0%, #111 100%);background:linear-gradient(to bottom, #585858 0%, #111 100%)}.dataTables_wrapper .dataTables_paginate .paginate_button:active{outline:none;background-color:#2b2b2b;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c));background:-webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-moz-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-ms-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-o-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:linear-gradient(to bottom, #2b2b2b 0%, #0c0c0c 100%);box-shadow:inset 0 0 3px #111}.dataTables_wrapper .dataTables_paginate .ellipsis{padding:0 1em}.dataTables_wrapper .dataTables_processing{position:absolute;top:50%;left:50%;width:100%;height:40px;margin-left:-50%;margin-top:-25px;padding-top:20px;text-align:center;font-size:1.2em;background-color:white;background:-webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255,255,255,0)), color-stop(25%, rgba(255,255,255,0.9)), color-stop(75%, rgba(255,255,255,0.9)), color-stop(100%, rgba(255,255,255,0)));background:-webkit-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-moz-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-ms-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-o-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:linear-gradient(to right, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%)}.dataTables_wrapper .dataTables_length,.dataTables_wrapper .dataTables_filter,.dataTables_wrapper .dataTables_info,.dataTables_wrapper .dataTables_processing,.dataTables_wrapper .dataTables_paginate{color:#333}.dataTables_wrapper .dataTables_scroll{clear:both}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody{*margin-top:-1px;-webkit-overflow-scrolling:touch}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>thead>tr>th,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>thead>tr>td,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>tbody>tr>th,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>tbody>tr>td{vertical-align:middle}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>thead>tr>th>div.dataTables_sizing,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>thead>tr>td>div.dataTables_sizing,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>tbody>tr>th>div.dataTables_sizing,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>tbody>tr>td>div.dataTables_sizing{height:0;overflow:hidden;margin:0 !important;padding:0 !important}.dataTables_wrapper.no-footer .dataTables_scrollBody{border-bottom:1px solid #111}.dataTables_wrapper.no-footer div.dataTables_scrollHead table.dataTable,.dataTables_wrapper.no-footer div.dataTables_scrollBody>table{border-bottom:none}.dataTables_wrapper:after{visibility:hidden;display:block;content:"";clear:both;height:0}@media screen and (max-width: 767px){.dataTables_wrapper .dataTables_info,.dataTables_wrapper .dataTables_paginate{float:none;text-align:center}.dataTables_wrapper .dataTables_paginate{margin-top:0.5em}}@media screen and (max-width: 640px){.dataTables_wrapper .dataTables_length,.dataTables_wrapper .dataTables_filter{float:none;text-align:center}.dataTables_wrapper .dataTables_filter{margin-top:0.5em}} ================================================ FILE: static/scss/explorer/table.scss ================================================ @import '../colors'; #wtm{ margin-top: 30px; margin-bottom: 30px; } .nav-tabs{ margin-bottom: 20px; } #export-data{ background: $dark-purple; color: white; float: right; } .dataTables_filter{ label { input { border: solid 1px $light-blue-gray !important; height: 31px !important; padding-left: 5px !important; width: 350px; } } } .dataTables_length{ label { select { background: none; border: none !important; } } } .dataTable{ td { border: none !important; } th { border: none !important; } } ================================================ FILE: static/scss/home/index.scss ================================================ @import '../colors'; $font-serif: Palatino, "Palatino Linotype", "Palatino LT STD", "Book Antiqua", Georgia, serif; /* Header */ .home-header { padding-top: 60px; background-color: $blue-color; margin-top: 0px; padding-bottom: 60px; h1 { color: #fff; font-size: 38px; margin-bottom: 20px; max-width: 480px; font-weight: 300; line-height: 1.3; } p { color: rgba(255, 255, 255, 0.7); font-size: 13px; margin-top: 20px; span { display: block; margin-bottom: 7px; } } .btn { border-radius: 0; box-shadow: 0px 1px 2px 0px rgba(0,0,0,0.11); padding: 12px; margin-top: 10px; &:hover, &:focus { color: #fff; } } a { color: #fff; } .fa { padding-right: 5px; } } .trackers-img img { max-width: 100%; } .btn-grey { background-color: #7b939b; border-color: #ccc; border: 0px; border-radius: 0; color: #fff; padding: 12px 17px; } .header-thumbs { margin-top: 70px; } .header-articles { position: relative; &:before { display:block; content: ""; position:absolute; background-color: $blue-color; width:100%; height: 198px; } h3 { font-size: 19px; padding-bottom: 10px; margin-top: 5px; a { color: $black-color; } } p { color: $grey-txt-color; } .caption { padding: 30px; min-height: 200px; max-height: 200px; .primer{ font-size: 10px; text-transform: uppercase; letter-spacing: 1px; font-weight: bold; color: $red-txt-color; margin-top: -13px; } .article{ font-size: 10px; text-transform: uppercase; letter-spacing: 1px; font-weight: bold; color: $grey-txt-color; margin-top: -13px; } } } .svg-container { border-radius: 4px; box-shadow: 8px 14px 38px rgba(39,44,49,0.06), 1px 3px 8px rgba(39,44,49,0.03); } .plot-caption{ font-size: 14px; color: $grey-txt-color; margin-top: 10px; font-family: 'FiraMono-Regular', monospace; } .home-plot { color: $grey-txt-color; h4 { font-size: 11px; color: $grey-txt-color; margin-top: 25px; } a { color: $grey-txt-color; } .plot-subtitle { margin-bottom: 30px; } .plot-title { font-size: 23px; font-weight: bold; text-transform: uppercase; color: $black-color; } .checkout { i { vertical-align: top; margin-right: 5px; padding-top: 4px; } span { display: inline-block; max-width: 170px; } } } .home-panel .panel { color: $grey-txt-color; background: none; box-shadow: none; border: 0px; } .home-panel h3 { font-size: 23px; line-height: 1.3; } .home-panel p { font-size: 16px; } .home-panel .card-cta { color: $black-color; font-size: 18px; } .home-panel .label-default { border-radius: 12px; background-color: transparent; color: $grey-txt-color; border: 1px solid #ccc; font-size: 13px; } .card-purple { position: relative; } .card-purple:before { content: '▮'; display: block; position: absolute; left: -20px; font-size: 25px; top: -6px; color: $red-txt-color; } .card-gray { position: relative; } .card-gray:before { content: '▮'; display: block; position: absolute; left: -20px; font-size: 25px; top: -6px; color: $graph-gray; } .most-trackers{ border-bottom: solid 1px $light-blue-gray; } .most-websites { padding-bottom: 40px; border-bottom: solid 1px $light-blue-gray; .most-tracked-websites { h3 { margin-bottom: 15px; } h2 .subtitle { font-size: 13px; } #multi-column-list { li { a { color: $link-color; } img { margin-right: 5px; } .count { color: $red-txt-color; border: 1px solid #ccc; padding: 0 2px; font-weight: 600; } .subtitle { text-transform: uppercase; font-size: 13px; } .all-websites { padding-left: 23px; } } } } .websites-info-box { border: 4px solid $red-txt-color; padding: 20px; margin-top: 20px; h4 { color: $grey-txt-color; font-size: 13px; padding-left: 15px; } .percentage { font-size: 45px; color: $red-txt-color; line-height: 1; font-family: 'FiraMono-Regular', monospace; } .mb { font-size: 23px; } p { color: $black-color; font-size: 16px; } img { margin-bottom: 5px; } .tracking-method { font-size: 13px; color: $grey-txt-color; } .learn-more { color: $grey-txt-color; } } } .most-trackers { #multi-column-list a { font-family: 'FiraMono-Regular', monospace; &.top-trackers { color: $grey-txt-color; } } } ================================================ FILE: static/scss/trackers/list.scss ================================================ @import '../colors'; .trackers-list { &.dark-page { h1 { color: #fff; font-family: 'FiraMono-Bold', monospace; font-size: 35px; span.sub-title { font-family: 'Open Sans', sans-serif; font-size: 13px; position: relative; left: 0px; text-transform: uppercase; } } .subtitle { color: rgba(255, 255, 255, 0.7); } .list-container { padding: 20px 40px 0; background-color: #fff; #multi-column-list { margin-left: -40px; margin-right: -40px; padding-top: 40px; background-color: #f4f7f8; padding-left: 40px; margin-bottom: 100px; padding-bottom: 50px; } } } .subtitle { float: left; margin-right: 12px; margin-top: 3px; } } .top-5-trackers { .top-5-list { list-style: none; padding-left: 0; li { border-bottom: 1px solid #eee; padding-top: 15px; &:last-child { border-bottom: 0; } span { color: $grey-txt-color; } .highlight { color: $red-txt-color; } a { color: $red-txt-color; font-size: 16px; } p { margin-left: 15px; max-width: 260px; margin-top: 5px; } .category { margin-left: 10px; margin-right: 10px; } .label-default { background-color: #fff; border: 1px solid #CFD8DB; border-radius: 12px; color: $grey-txt-color; font-weight: normal; } } } .info-box { border: 4px solid $red-txt-color; padding: 38px 40px; margin-top: 20px; .percentage { font-size: 45px; color: $red-txt-color; line-height: 1; font-family: 'FiraMono-Regular', monospace; } p { color: $black-color; font-size: 16px; margin-bottom: 36px; &:last-child { margin-bottom: 0; } } } } .list-container ul a { font-family: 'FiraMono-Regular', monospace; } ================================================ FILE: static/scss/trackers/profile.scss ================================================ @import '../colors'; #tracker-page { #overview-header { background-color: $dark-header; .tracker-rank { font-family: 'FiraMono-Regular', monospace; border-bottom: 1px solid rgba(255, 255, 255, 0.2); padding-bottom: 4px; .emphasis { font-size: 25px; margin-right: 5px; } } .description { font-size: 13px; } #operates-under { p { font-size: 13px; } p:nth-of-type(1n+3) { display: none; } .operates-expand, .operates-collapse { color: rgba(255, 255, 255, 0.5); font-size: 13px; } i { margin-right: 5px; } } } .overview-info { margin-bottom: 17px; h1 { font-family: 'FiraMono-Bold', monospace; font-size: 27px; text-transform: uppercase; margin-top: 30px; margin-bottom: 0px; } .overview-info-right { margin-top: 9px; } } .overview-header-box { h3 { position: relative; } .trackers-icon { width: 28px; margin-right: 10px; float: left; } .trend{ margin-bottom: 42px; } .percentage { position: relative; font-size: 32px; .graph { width: 153px; display: inline-block; position: absolute; top: -20px; margin-top: -13px; } } .description { position: relative; margin-left: 45px; } .subtitle { font-size: 13px; color: $grey-txt-color; margin-top: 20px; } .progress { background: none; box-shadow: none; max-width: 400px; margin-bottom: 5px; border-radius: 0; } .progress-bar { white-space: nowrap; border-radius: 0px; text-align: left; background: $inactive-gray; color: #000; padding:0 8px; box-shadow: none; } .progress-presence { float: left; margin-right: 10px; width: 20px; color: $red-txt-color; font-size: 13px; font-family: "FiraMono-Bold", monospace; } .right-column { padding-left: 0px; } } #similar-trackers { color: $grey-txt-color; background-color: $light-blue-bg; padding-top: 20px; padding-bottom: 30px; border-bottom: solid 1px $light-blue-gray; .cat-item { margin-top: 10px; margin-bottom: 15px; } ul { list-style: none; padding-left: 0; margin-top: 20px; li { margin-bottom: 14px; } a { color: $red-txt-color; font-size: 16px; font-family: 'FiraMono-Regular', monospace; } } h4 { font-size: 14px; margin-top: 25px; } h3 { line-height: 1.3; margin-bottom: 20px; } p { margin-bottom: 0; } .tracker-category { a { color: $link-color; font-size: 13px; } } .tracker-cat .cat-item { border: 1px solid #ccc; border-radius: 20px; padding: 2px 6px 2px; margin-bottom: 2px; display: inline-block; font-size: 13px; } .tracker-cat{ margin-bottom: 10px !important; } .tracker-desc{ margin-bottom: 42px; } } #tag-cloud { margin-top: 20px; padding-bottom: 100px; border-bottom: solid 1px $light-blue-gray; h2 { margin-bottom: 20px; } .nav-tabs { margin-bottom: 30px; } } } ================================================ FILE: static/scss/websites/overview.scss ================================================ @import '../colors'; .websites-page { .info-box { margin-top: 40px; margin-bottom: 10px; } hr { border-bottom: 3px solid $red-txt-color; } .subtitle { float: left; margin-right: 12px; } #multi-column-list li { .count { color: $red-txt-color; border: 1px solid $light-purple-border; padding: 0 2px; font-family: 'FiraMono-Bold', monospace; } a { color:#0078CA; } .label-category { text-transform: uppercase; border: none; } } } ================================================ FILE: static/scss/websites/profile.scss ================================================ @import '../colors'; /* WEBSITES */ #website-page .overview-header-box { #doughnut { height: 200px; } .doughnut-legend { list-style: none; margin-top: 20px; padding-left: 0; width: 170px; } .cat-item { border: 1px solid #ccc; border-radius: 20px; padding: 2px 6px 2px; margin-bottom: 2px; display: inline-block; font-size: 13px; } .doughnut-legend i { font-size: 12px; line-height: 12px; padding-right: 2px; } .stats{ margin-bottom: 42px; } } #multi-column-list { a { font-family: 'FiraMono-Regular', monospace; } .percentage { font-size: 13px; color: $red-txt-color; font-family: 'FiraMono-Regular', monospace; } } #website-page { .plot-label { color: $grey-txt-color; } .plot-subtitle { color: $red-txt-color; } } ================================================ FILE: templates/base.html ================================================ {% block title %} {% endblock %} {% block og_params %} {% endblock %} {% block extra_styling %} {% endblock %} {% include "components/navbar.html" %} {% include "components/breadcrumb.html" %} {% block content %} {% endblock %} {% include "components/breadcrumb.html" %} {% include "components/footer.html" %} ================================================ FILE: templates/blog-page.html ================================================ {% extends "base.html" %} {% block title %} Blog | {{blog_post.title}} {% if blog_post.redirect_url %} {% endif %} {% endblock %} {% block og_params %} {% endblock %} {% set active_page='blog' %} {% set active_page_name=blog_post.title %} {% block extra_styling %} {% endblock %} {% block content %}

{{ blog_post.title }}

{{ blog_post.subtitle }}


{{ blog_post.body | markdown }}
{% endblock %} ================================================ FILE: templates/blog.html ================================================ {% extends "base.html" %} {% block title %} WhoTracks.me | Blog {% endblock %} {% block og_params %} {% endblock %} {% block extra_styling %} {% endblock %} {% set active_page='blog' %} {% block content %}

Blog

This is a blog space for tech teams at Ghostery.
{% for batch in blog_posts|batch(3) %}
{% for blog_post in batch %} {% if blog_post.publish %}
{% include "components/blog-card.html" %}
{% endif %} {% endfor %}
{% endfor %}
{% endblock %} ================================================ FILE: templates/company-page.html ================================================ {% extends "base.html" %} {% set active_page='companies' %} {% set active_page_name=demographics.name %} {% block content %}

Privacy Policy

{% endblock %} ================================================ FILE: templates/components/blog-card.html ================================================

{{blog_post['type']}}

{{ blog_post.title }}

{{ blog_post.description }}

================================================ FILE: templates/components/breadcrumb.html ================================================ {% if active_page %} {% if not active_page=="home" %} {% endif %} {% endif %} ================================================ FILE: templates/components/category-item.html ================================================
    {% for c in tracker_categories %}
  • {% include 'components/trackers/category.html' %}
  • {% endfor %}
================================================ FILE: templates/components/company-card.html ================================================
{% if demographics.logo is not none %}
{% else %}
{{ initials }}
{% endif %}

{{ demographics.website_url }}

{{ demographics.name }}

{% if demographics.description != "None" %} {{ demographics.description }} {% endif %}

================================================ FILE: templates/components/cookies.html ================================================

COOKIES are files placed by the website, stored in the browser that is used to identify you to the website.

================================================ FILE: templates/components/fingerprinting.html ================================================

FINGERPRINTING is a unique digital signature derived from the properties of your device.

================================================ FILE: templates/components/footer.html ================================================ ================================================ FILE: templates/components/home/header.html ================================================

Learn about tracking technologies, market structure and data-sharing on the web

WhoTracksMe Paper 2018

What is a tracker? Where does this data come from?

================================================ FILE: templates/components/navbar.html ================================================ ================================================ FILE: templates/components/tag_cloud.html ================================================
================================================ FILE: templates/components/top-5-info-box.html ================================================
{{header_stats.by_cookies|to_percentage}}%

Trackers using cookies.

{{header_stats.by_fingerprinting|to_percentage}}%

Trackers using fingerprinting.

{{header_stats.data|b_to_mb}}MB

average data usage by trackers

================================================ FILE: templates/components/top-5-trackers.html ================================================
    {% for tracker in tracker_list[:5] %}
  • {{loop.index}}. {{ tracker.id|get_app_name }} {% if tracker.company_id %} {{ tracker.company_id|get_company_name }} {% endif %} {% if tracker.category %} {% with c=tracker.category %} {% include 'components/trackers/category.html'%} {% endwith %} {% endif %}

    {{tracker.reach|to_percentage}}% of web traffic is tracked by {{ tracker.id|get_app_name }}

  • {% endfor %}
{%include "components/top-5-info-box.html" %}
================================================ FILE: templates/components/tracker-list.html ================================================
{% include "components/top-5-trackers.html" %}
    {% for tracker in tracker_list[5:] %}
  • {{loop.index + 5}}. {{ tracker.id|get_app_name }} {% if tracker.company_id %} {{ tracker.company_id|get_company_name }} {% endif %}
  • {% endfor %}
{% with tracker_list=trackers_list_company %} {% include "components/top-5-trackers.html" %} {% endwith %}
    {% for tracker in trackers_list_company[5:] %}
  • {{loop.index + 5}}. {{ tracker.id|get_app_name }} {% if tracker.company_id %} {{ tracker.company_id|get_company_name }} {% endif %}
  • {% endfor %}
================================================ FILE: templates/components/trackers/category.html ================================================ {{ c|prettify_label }} ================================================ FILE: templates/components/trackers/header.html ================================================

{{ profile.name }}

{% if app.company_id %}

Owned by {{app.company_id|get_company_name}}

{% endif %} {% if profile.website_url is not none %}

{{ profile.website_url }}

{% endif %}

TRACKER RANK

{{ profile.overview.reach_rank }} /{{ trackers }}

{{profile.id|rank_label}}

{% if profile.domains | length > 0 %}

OPERATES UNDER

{% for domain in profile.domains %}

{{ domain }}

{% endfor %} {%if profile.domains|length > 2%} Expand {% endif %}
{% endif %}

TRACKER REACH

{{ reach.pages }}% {{ trends.page }}

of web traffic is tracked by {{ profile.name }}

{{ reach.sites }} {{ trends.site }}

of the top 10,000 sites seen loading the {{ profile.name }} tracker

{% if profile.date_range[0] == profile.date_range[1] %} Data from {{ profile.date_range[0].strftime('%B %Y') }}. {% else %} Data from {{ profile.date_range[0].strftime('%B %Y') }} to {{ profile.date_range[1].strftime('%B %Y') }}. {% endif %}

SEEN ON THESE TYPES OF WEBSITES

{% for tup in website_types %} {% if tup[0] %}
{{ tup[1]|int }}%
{{ tup[0] }}
{% endif %} {% endfor %}

TRACKING TYPE

{% if tracking_methods.cookies %}
{% include "components/cookies.html" %}
{% endif %} {% if tracking_methods.fingerprinting %}
{% include "components/fingerprinting.html" %}
{% endif %} {% if not tracking_methods.cookies and not tracking_methods.fingerprinting %}

No tracking detected at present

{% endif %}
================================================ FILE: templates/components/tracking-methods.html ================================================

TRACKING METHODS

{% if site.overview.cookies > 0.4 and site.overview.bad_qs > 0.1 %}
{% include "components/cookies.html" %}
{% include "components/fingerprinting.html" %}
{% elif site.overview.cookies > 0.1 %}
{% include "components/cookies.html" %}
{% elif site.overview.bad_qs > 0.1 %}
{% include "components/fingerprinting.html" %}
{% else %}

No tracking detected on this site at present.

{% endif %} ================================================ FILE: templates/components/unified-ui-tracker-list.html ================================================
    {% for tracker in trackers_list_company %}
  • {{loop.index}}. {{ tracker.tracker|get_app_name }} {% if tracker.company_id %} {{ tracker.company_id|get_company_name }} {% else %} - {% endif %}
  • {% endfor %}
================================================ FILE: templates/components/website-list.html ================================================
================================================ FILE: templates/components/websites/header.html ================================================
{% if profile.website_url is not none %}

{{ profile.name | normalize_domain_name }}

{% else %}

not a real domain

{% endif %}

{{ site.overview.content_length|b_to_mb }}Mb of user data from trackers per page

{{ site.overview.trackers|round2 }} TRACKERS
On average per page

ALL TRACKERS SEEN (by category)

{{ doughnut }}
{% include "components/category-item.html" %}
{% include "components/tracking-methods.html" %}

PROPORTION OF TRAFFIC

{{ (100*site.overview.tracked)|round }}%

Page loads from {{ profile.website_url | normalize_domain_name }} on which tracking occurred

REQUESTS TO TRACKERS

{{ site.overview.requests_tracking|int }}

Tracking requests per page load

================================================ FILE: templates/components/websites/tracker-list.html ================================================
    {% for tracker in tracker_list|sort(attribute='site_proportion', reverse=True) %}
  • {{loop.index}}. {{ tracker.tracker|get_app_name }} {{tracker.site_proportion|to_percentage}}% {% if tracker.company_id %} {{ tracker.company_id|get_company_name }} {% else %} - {% endif %}
  • {% endfor %}
    {% for tracker in tracker_list|sort(attribute='company_id') %}
  • {{loop.index}}. {{ tracker.tracker|get_app_name }} {{tracker.site_proportion|to_percentage}}% {% if tracker.company_id %} {{ tracker.company_id|get_company_name }} {% else %} - {% endif %}
  • {% endfor %}
================================================ FILE: templates/explorer.html ================================================ {% extends "base.html" %} {% block title %} WhoTracks.me | Explorer {% endblock %} {% block og_params %} {% endblock %} {% set active_page='explorer' %} {% block extra_styling %} {% endblock %} {% block content %}

Definitions for the column names are available here.

{% endblock %} ================================================ FILE: templates/imprint.html ================================================ {% extends "base.html" %} {% block title %} WhoTracks.me, operated by Ghostery - Privacy Policy {% endblock %} {% block og_params %} {% endblock %} {% block content %}

Imprint



WhoTracks.Me is a project of Ghostery GmbH, Arabellastraße 23, 81925 Munich, who owns and operates the website www.whotracks.me.

Ghostery GmbH is registered under the registration HRB 230794 at the Registration Court Amtsgericht Munich – Registergericht –, Infanteriestraße 5, 80325 Munich.

Registered Directors (vertretungsberechtigte Geschäftsführer) are Jean Paul Schmetz and Heinz Spengler.

The VAT ID Number is DE313473689.

For communication you can either visit our get help page or write an e-mail to support@ghostery.com

{% endblock %} ================================================ FILE: templates/index.html ================================================ {% extends "base.html" %} {% block title %} WhoTracks.me - Bringing Transparency to Online Tracking {% endblock %} {% block og_params %} {% endblock %} {% set active_page='home' %} {% block extra_styling %} {% endblock %} {% block content %} {% include "components/home/header.html" %}
{% for blog_post in posts %}
{% include "components/blog-card.html" %}
{% endfor %}

Tracker Market Share

Proportion of the web traffic tracked by these companies.

READ OUR PAPER:

WhoTracksMe 2018
{{ ts }}

See Full Chart

Google trackers are present on {{ top10[0].reach |to_percentage|round(0)|int }}% of the web traffic

That is more than the next 4 biggest trackers combined.

#trackerTopia #growth #topSites

{{ top10[1].reach |to_percentage|round(0)|int }}% of the web has a hidden Facebook tracking pixel

Facebook knows more than what you just do on Facebook

#facebook #trackingPixel #reach

{{ tracker_stats.gt01 }} companies track more than 0.1% of the web traffic each.

Which of these have you heard of?

#unknown #pervasive #mydata

Websites with the most (and least) tracking

TRACKER PRESENCE

{{ websites.gt10 }}

out of {{ websites.count }} top websites have more than 10 trackers per page.

{{ (websites.data / 1024 / 1024)|round|int }}MB

of data per page load on average required by trackers

{% include "components/fingerprinting.html" %}
{% include "components/cookies.html" %}

The most common trackers

{% include "components/unified-ui-tracker-list.html" %}
{% endblock %} ================================================ FILE: templates/not-found.html ================================================ {% extends "base.html" %} {% block title %} WhoTracks.me | Page Not Found {% endblock %} {% block og_params %} {% endblock %} {% block content %}

Page not Found

We could not find the page you are looking for. We have lots of other interesting things for you to see though. Did you check our trackers page?

{% endblock %} ================================================ FILE: templates/privacy-policy.html ================================================ {% extends "base.html" %} {% block title %} WhoTracks.me - Bringing Transparency to Online Tracking {% endblock %} {% block og_params %} {% endblock %} {% block content %}

WhoTracks​.​Me Privacy Policy

Please find below our statement on the processing of personal data by our company in accordance with the legal requirements, particularly the EU General Data Protection Regulation (GDPR - available here).

Content

I. General information

This section of the data privacy statement contains information on the scope of validity, the person responsible for data processing (controller), the data protection officer and data security. It also begins with a list of definitions of important terms used in the data privacy statement.

1. Definition of main terms

Browser: Computer program used to display websites (e.g. Chrome, Firefox, Safari)

Cookies: Text files placed on the user’s computer by the web server by means of the browser which is used. The stored cookie information may contain both an identifier (cookie ID) for recognition purposes and content data, such as login status or information about websites visited. The browser sends the cookie information back to the web server with each new request upon subsequent repeat visits to these sites. Most browsers accept cookies automatically. Cookies can be managed using the browser functions (usually under “Options” or “Settings”). The storage of cookies may be disabled in this way or it may be made dependent on the user’s approval in any given case or otherwise restricted. Cookies may also be deleted at any time.

Third countries: Countries outside of the European Union (EU)

GDPR: Regulation (EU) 2016/679 of the European Parliament and of the Council of 27 April 2016 on the protection of natural persons with regard to the processing of personal data and on the free movement of such data, and repealing Directive 95/46/EC (General Data Protection Regulation), available here.

Personal data: Any information relating to an identified or identifiable natural person. An identifiable natural person is one who can be identified, directly or indirectly, in particular by reference to an identifier, such as a name, an identification number, location data, an online identifier or to one or more factors specific to the physical, physiological, genetic, mental, economic, cultural or social identity of that natural person.

Profiling: Any form of automated processing of personal data consisting of the use of personal data to evaluate certain personal aspects relating to a natural person, in particular to analyse or predict aspects concerning that natural person’s performance at work, economic situation, health, personal preferences, interests, reliability, behaviour, location or movements.

Services: Our offers to which this data privacy statement applies (cf. Scope of validity).

Tracking: The collection of data and their evaluation regarding the behaviour of visitors in response to our services.

Tracking technologies: Actions can be tracked either via the activity records stored on our web servers (log files) or by collecting data from end devices via pixels, cookies or similar tracking technologies.

Processing: Any operation or set of operations which is performed on personal data or on sets of personal data, whether or not by automated means, such as collection, recording, organisation, structuring, storage, adaptation or alteration, retrieval, consultation, use, disclosure by transmission, dissemination or otherwise making available, alignment or combination, restriction, erasure or destruction.

Pixel: Pixels are also called tracking pixels, web beacons or web bugs. These are small, invisible graphics in HTML emails or on websites. When a document is opened, this small image is downloaded from a server on the Internet and the download is registered there. This allows the operator of the server to see if and when an email has been opened or a website has been visited. This function is usually carried out by calling up a small program (JavaScript). Certain types of information can be detected on your computer system in this way and shared, such as the content of cookies, the time and date of the visit, and a description of the page on which the tracking pixel is located.

2. Scope of validity

This data privacy statement applies to the following services:

  • Our online offering “WhoTracks​.​Me” (website), mainly available at https://whotracks.me
  • Whenever reference is made to this data privacy statement from one of our offers (e.g. websites, subdomains, links to third-party sites), regardless of the way in which it is accessed or used.

All of these offers are also collectively referred to as “services”.

3. Controller

The following party is responsible for the processing of data in relation to the services, i.e. the role of controller which involves determining the purposes and means of processing personal data:

Ghostery GmbH
Arabellastraße 23
81925 München, Germany
Email: info@ghostery.com

4. Data protection officer

The contact details of our data protection officer are given in paragraph 3. Messages should be marked for the attention of the data privacy department or sent via privacy@ghostery.com.

II. Details of data processing

1. General information about the data processing operations

The following applies to all the processing operations listed below, unless stated otherwise:

a) No obligation to provide personal data & consequences of failure to provide such data

The provision of personal data is not required by law or contract, and you are under no obligation to provide any data. We will inform you during the data entry process when personal information must be provided for the relevant service (e.g. by indicating “mandatory field”). In cases where the provision of data is required, the consequence of not providing data will be that the service in question cannot be provided. Otherwise, failure to provide data may result in our inability to provide our services in the same form and quality.

In various cases, you may also grant us your consent to the further processing of data (or some of the data, where applicable) in connection with the operations listed below. In this case, we will inform you separately in connection with the submission of the respective declaration of consent about all the procedures and the scope of the consent and concerning the purposes which we pursue in these processing operations. The processing operations based on your consent are therefore not listed again here (Art. 13 (4) GDPR).

c) Transfer of personal data to third countries

When we send data to third countries, i.e. countries outside of the European Union, the data are then transmitted strictly in compliance with the statutory conditions of admissibility. If the transmission of the data to a third country does not serve the purpose of fulfilling our contract with you, if we do not have your consent, if the transmission is not required for asserting, exercising or defending legal claims, and if no other exemption applies under Art. 49 GDPR, we will only transmit your data to a third country if in possession of an adequacy decision pursuant to Art. 45 GDPR or appropriate guarantees under Art. 46 GDPR. In order to ensure an adequate level of data protection, we provide appropriate safeguards pursuant to Art. 46 (2) c) GDPR by the conclusion of EU standard data protection clauses adopted by the European Commission with the receiving body. Copies of the standard EU data protection clauses are available on the website of the European Commission here.

d) Hosting at external service providers

Our data processing work is carried out to a large extent with the involvement of hosting service providers who provide us with storage space and processing capacities at their data centres and who also process personal data on our behalf according to our instructions. It may be the case that personal data are transmitted to hosting service providers in respect of all of the functions listed below. These service providers process data either exclusively in the EU or subject to guaranteed levels of data protection which we have put in place based on the standard EU data protection clauses (cf. subsection c).

e) Transmission to government authorities

In principle, we do not transmit any data to government authorities. We only send personal information to government authorities (including law enforcement agencies) when required to fulfil a legal obligation to which we are subject (legal basis: Art. 6 (1) c) GDPR) or when it is necessary for the assertion, exercise or defence of legal claims (legal basis: Art. 6 (1) f) GDPR).

f) Period of storage

The time specified in the “period of storage” paragraph indicates how long we use the data for the relevant purposes in any given case. At the end of this period, the data will no longer be processed by us but will be erased at regular intervals, unless continued processing and storage are required by law (mainly because it is necessary to fulfil a legal obligation or for the establishment, exercise or defence of legal claims) or unless you grant us extended consent.

g) Data categories

The category names listed below are used for specific types of data in the following sections:

  • Contact data: Email address(es)
  • Access data: Date and time of visit to our services; the page from which the system accessed our site (“referrer”); information about the interaction of logged in users with our products; session identification data (session ID), as well as the following information relating to the computer system accessing the service: Internet Protocol address used (IP address), browser type, browser version, device type, operating system and similar technical information.

2. Accessing our services

The passages below set out how your personal data are processed when you access our services (e.g., loading and viewing the website, opening the mobile app and navigating within the app). We would point out that it is impossible not to send access data to external content providers (cf. subsection b) due to the technical processes involved in transmitting information over the Internet. The third-party providers are themselves responsible for the privacy-compliant operation of the IT systems which they use. The service providers are required to decide how long the data will be stored.

  • Data category:

    Access data

  • Purpose:

    Establishing connection; presenting contents of the service; detecting attacks on our site due to unusual activities; fault diagnosis

  • Legal basis:

    Art. 6 (1) f) GDPR Our legitimate interest: Proper functioning of the services; security of data and business processes; prevention of misuse; prevention of damage through interference in information systems

  • Period of storage:

    Four weeks

b) Recipients of the personal data

  • Recipient category:

    External content providers who provide content which is needed to display the service (e.g. images, videos, embedded postings from social networks, banner ads, fonts, update information, shortened links) as well as IT Security Service Provider

  • Data concerned:

    Access data

  • Legal Basis:

    Art. 6 (1) f) GDPR Our legitimate interest: Proper functioning of the services; (accelerated) display of content; Prevention of attacks through exploitation of security gaps/vulnerabilities

    Email address; Personal master data; Newsletter usage profile data

  • Purpose:

    Verification of the registration process (“double opt-in”) including traceability of registrations and unsubscriptions (“logging”); sending and designing the newsletter according to interests; measurement of opening and click rates for the purpose of optimising our newsletter service.

  • Period of storage:

    Personal data is deleted as soon as its further processing is no longer necessary for the respective purpose and legal retention periods do not prevent deletion. This is regularly the case upon receipt of your withdrawal. In the event of your withdrawal, however, we reserve the right to store your e-mail address for the purpose of proving that you have previously given your consent. This storage is solely for the purpose of defending possible legal claims.

3. Downloads (tracker data)

The download of our complete data set on the largest and longest measurement of online tracking can be performed at Github. For this purpose we link directly to our repository.

4. Contacting WhoTracks.Me

We invite our visitors to send us an email for any question or concern they might have. The tables below show how your personal data are processed when you contact our customer support.

  • Data category:

    Personal master data; contact details; e-mail address; contents of enquiries/complaints

  • Purpose:

    Processing of customer feedback, enquiries, and user complaints

  • Legal basis:

    Art. 6 (1) b) and f) GDPR

    Our legitimate interest: Improvement of our service; increase in customer loyalty

  • Period of storage:

    We retain any personal data related to user-submitted email during the processing of the inquiry. We delete these tickets after 6 months of inactivity.

b) Recipients of the personal data

  • Recipient category:

    IT service providers

  • Data concerned:

    All data listed under (a) in this section

  • Legal Basis:

    Art. 28 GDPR

5. Web Analytics

This website uses no analytics.

III. Rights of data subjects

1. Right to object

If we process your personal data for direct marketing purposes, you have the right to object at any time to the processing of your personal data for such marketing with future effect.

You also have the right, at any time with future effect and for reasons pertinent to your particular situation, to object to the processing of your personal data in accordance with Art. 6 (1) e) or f) GDPR; this also applies to any profiling based on these provisions. The right to object may be exercised free of charge. In order to be able to process your request faster, please reach us by emailing us at privacy@ghostery.com.

2. Right of access

You have the right to obtain confirmation from us as to whether or not personal data concerning you are being processed and, where that is the case, to access the personal data and the other information listed in Art. 15 GDPR.

3. Right to rectification

You have the right to obtain from us without undue delay the rectification of incorrect personal data concerning you (Art. 16 GDPR). Taking into account the purposes of the processing, you have the right to have incomplete personal data completed, including by means of providing a supplementary statement.

4. Right to erasure (“right to be forgotten”)

You have the right to obtain from us the erasure of personal data concerning you without undue delay if one of the grounds listed in Art. 17 (1) GDPR is applicable and the processing operations are not required for one of the purposes approved in Art. 17 (3) GDPR.

5. Right to restriction of processing

You are entitled to obtain from us the restriction of the processing of your personal data where one of the conditions laid down in Art. 18 (1) a) to d) GDPR is met.

6. Right to data portability

You have the right, in respect of the personal data which you have given us, to be provided with these data in a structured, commonly used and machine-readable format and the right to send these data to another controller without any hindrance on our part, insofar as the requirements set out in Art. 20 (1) GDPR are met. In exercising your right to data portability, you have the right to have the personal data transmitted directly by us to another controller where technically feasible.

If the processing is based on your consent, you have the right to revoke your consent at any time. This will not affect the legality of the processing operations on the basis of the consent until such time as the revocation takes effect.

8. Right to object

You have the right to lodge a complaint with the supervisory authority responsible for our company. The supervisory authority responsible for our company is as follows:

Bayerisches Landesamt für Datenschutzaufsicht
Promenade 18
91522 Ansbach
poststelle@lda.bayern.de

IV. California

For residents of California, please see our Privacy Policy Supplemental Notice – California.

{% endblock %} ================================================ FILE: templates/reach-chart-page.html ================================================ {% extends "base.html" %} {% block title %} WhoTracks.me | Who Tracks the Most {% endblock %} {% block extra_styling %} {% endblock %} {% block content %}

Company Tracking Reach

Proportion of the web traffic tracked their trackers.

READ OUR PAPER:

WhoTracksMe 2018
{{ chart }}

Generated from Ghostery Anti-Tracking data

{% endblock %} ================================================ FILE: templates/tracker-not-found.html ================================================ {% extends "base.html" %} {% block title %} WhoTracks.me | Tracker Not Found {% endblock %} {% block og_params %} {% endblock %} {% block content %}

Tracker Profile missing

This tracker is not very popular, hence we don't have a profile on it yet. We are constantly adding more data on trackers, so make sure you check again in the future. Meanwhile, have you seen our list of trackers?

{% endblock %} ================================================ FILE: templates/tracker-page.html ================================================ {% extends "base.html" %} {% block title %} Tracker | {{ app.tracker | get_app_name }} {% endblock %} {% block og_params %} {% endblock %} {% set active_page='trackers' %} {% set active_page_name=profile.name %} {% block extra_styling %} {% endblock %} {% block content %}
{% include "components/trackers/header.html" %}

TRACKER CATEGORY

{% if profile.category %} {{profile.category|prettify_label}}

{{CATEGORY_DESC[profile.category]}}

{% else %} misc

{{ CATEGORY_DESC["misc"]}}

{% endif %}

Learn about our tracker categories

What is a tracker?

SIMILAR TRACKERS

    {% for tracker in similar_trackers %}
  • {{ tracker.id|get_app_name }} {% if tracker.company_id %} {{ tracker.company_id|get_company_name }} {% endif %}
  • {% endfor %}

Presence on top sites

{% if sites_by_cat|length > 0 %} {% include "components/tag_cloud.html" %} {% endif %}
{% endblock %} ================================================ FILE: templates/trackers.html ================================================ {% extends "base.html" %} {% block title %} WhoTracks.me | Trackers {% endblock %} {% block og_params %} {% endblock %} {% set active_page='trackers' %} {% block content %} {% block extra_styling %} {% endblock %}

TRACKERS RANK Top {{ tracker_list|count }} most prevalent trackers on the web

Sort by:
{% include 'components/tracker-list.html' %}
{% endblock %} ================================================ FILE: templates/website-not-found.html ================================================ {% extends "base.html" %} {% block title %} WhoTracks.me | Website Profile Not Found {% endblock %} {% block og_params %} {% endblock %} {% block content %}

Website Profile missing

We don't have a profile of this site yet. We are constantly adding more data on popular sites, so make sure you check again in the future. Meanwhile, have you seen our list of websites?

{% endblock %} ================================================ FILE: templates/website-page.html ================================================ {% extends "base.html" %} {% block title %} Website | {{ profile.name }} {% endblock %} {% block og_params %} {% endblock %} {% set active_page='websites' %} {% set active_page_name=profile.name %} {% block extra_styling %} {% endblock %} {% block content %}
{% include "components/websites/header.html" %}
{% if tracker_list|length > 0 %}

TRACKER MAP

TRACKER CATEGORIES
TRACKING COMPANIES
{{ sankey }}

Trackers Seen

{% include "components/websites/tracker-list.html" %}

{% endif %}
{% endblock %} ================================================ FILE: templates/websites.html ================================================ {% extends "base.html" %} {% block title %} WhoTracks.me | Websites {% endblock %} {% block og_params %} {% endblock %} {% set active_page='websites' %} {% block extra_styling %} {% endblock %} {% block content %}
{{header_numbers.have_trackers|to_percentage}}%

Proportion of traffic to top {{ website_list|count }} sites containing trackers

{{header_numbers.average_nr_trackers|int}}

Average number of trackers present on a site

{{header_numbers.tracker_requests}}

Average number of requests per page that track you


Number of trackers on the most visited domains

Sort by:
{% include 'components/website-list.html' %}
{% endblock %} ================================================ FILE: tests/__init__.py ================================================ ================================================ FILE: tests/test_data_integrity.py ================================================ from operator import itemgetter import unittest from whotracksme.data import load_tracker_db, DataSource class TestDataIntegrity(unittest.TestCase): def setUp(self): self.conn = load_tracker_db() self.ds = DataSource() def test_all_trackers_have_db_entry(self): trackers = self.ds.trackers cur = self.conn.cursor() app_ids = [row.id for row in trackers.get_snapshot()] db_trackers = cur.execute('select id, category_id from trackers where id IN ({}) order by id'.format( ','.join(["'{}'".format(id) for id in app_ids]) )).fetchall() self.assertEqual(set(app_ids), set(map(itemgetter(0), db_trackers))) # check all have a category without_category = list(filter(lambda tracker: tracker[1] is None, db_trackers)) self.assertEqual([], without_category) def test_all_companies_have_db_entry(self): companies = self.ds.companies cur = self.conn.cursor() company_ids = sorted([row.id for row in companies.get_snapshot()]) # company_id can be a company id or tracker id db_companies = cur.execute('select id from companies where id IN ({}) order by id'.format( ','.join(["'{}'".format(id) for id in company_ids]) )).fetchall() db_trackers = cur.execute('select id, category_id from trackers where id IN ({}) order by id'.format( ','.join(["'{}'".format(id) for id in company_ids]) )).fetchall() self.assertEqual(set(company_ids), set(map(itemgetter(0), db_companies)) | set(map(itemgetter(0), db_trackers))) if __name__ == '__main__': unittest.main() ================================================ FILE: tests/test_db_integrity.py ================================================ import unittest from whotracksme.data import load_tracker_db class TestDbIntegrity(unittest.TestCase): def setUp(self): self.conn = load_tracker_db() def test_all_trackers_have_domains(self): domainless_trackers = self.conn.execute('''select id from (select id, tracker_domains.tracker from trackers left join tracker_domains ON trackers.id = tracker_domains.tracker WHERE alias IS NULL) where tracker is NULL''').fetchall() self.assertEqual(domainless_trackers, []) ## Disabled because TrackerDB has "archived" entries, which have no active patterns. ## In principle, filtering the "archived" ones out would be safe; but it is neither ## sufficent nor possible with only the trackerdb.sql file. ## Overall, maintaining this requirement here does not seem to be worth the effort. ## Instead, whotracks.me should be able to operate with organizations without pattern. ## # def test_companies_have_trackers(self): # childless_companies = self.conn.execute('''select id FROM # (select companies.id, trackers.id AS tid from companies left join trackers on trackers.company_id = companies.id) # where tid is null''').fetchall() # self.assertEqual(childless_companies, []) if __name__ == '__main__': unittest.main() ================================================ FILE: tests/test_db_validity.py ================================================ import sqlite3 import unittest from whotracksme.data import load_tracker_db class ValidateTrackerDatabase(unittest.TestCase): conn = None @classmethod def setUpClass(cls): cls.conn = load_tracker_db() def test_db_has_trackers(self): cur = self.conn.cursor() cur.execute('SELECT COUNT(*) FROM trackers') count = cur.fetchone()[0] self.assertGreater(count, 0) def test_db_has_companies(self): cur = self.conn.cursor() cur.execute('SELECT COUNT(*) FROM companies') count = cur.fetchone()[0] self.assertGreater(count, 0) def test_db_has_tracker_domains(self): cur = self.conn.cursor() cur.execute('SELECT COUNT(*) FROM tracker_domains') count = cur.fetchone()[0] self.assertGreater(count, 0) def test_no_trackers_without_domain(self): cur = self.conn.cursor() cur.execute('SELECT COUNT(DISTINCT tracker) FROM tracker_domains') domain_tracker_count = cur.fetchone()[0] # Only look at tracker groups, ignoring the ones that are (non-trivial) aliases. # Ignore self-referring aliases and treat them like non-aliased entries. cur.execute('SELECT COUNT(DISTINCT id) FROM trackers WHERE alias is NULL or (id = alias)') tracker_count = cur.fetchone()[0] self.assertEqual(domain_tracker_count, tracker_count) def test_tracker_ids_ascii(self): cur = self.conn.cursor() cur.execute('SELECT id FROM trackers') trackers = cur.fetchall() for tracker in trackers: try: tracker[0].encode('ascii') except: self.fail(f'"{tracker[0]}" is not ascii') def test_company_ids_ascii(self): cur = self.conn.cursor() cur.execute('SELECT id FROM companies') companies = cur.fetchall() for company in companies: try: company[0].encode('ascii') except: self.fail(f'"{company[0]}" is not ascii') def test_domains_ascii(self): cur = self.conn.cursor() cur.execute('SELECT domain FROM tracker_domains') domains = cur.fetchall() for domain in domains: try: domain[0].encode('ascii') except: self.fail(f'"{domain[0]}" is not ascii') if __name__ == '__main__': unittest.main() ================================================ FILE: tests/test_site_categories.py ================================================ import unittest import csv VALID_CATEGORIES = set([ "Adult", "Banking", "Business", "E-Commerce", "Entertainment", "Government", "Health", "News and Portals", "Political", "Recreation", "Reference" ]) def iterate_site_categories(): with open('./whotracksme/data/assets/site_categories.csv', 'r') as fp: reader = csv.reader(fp, delimiter=',') for line in reader: if reader.line_num == 1: continue yield line class TestSitesData(unittest.TestCase): def test_all_categories_are_valid(self): for site, category in iterate_site_categories(): self.assertIsNotNone(site) self.assertTrue('.' in site) self.assertIn(category, VALID_CATEGORIES) def test_no_repeated_sites(self): sites = set([]) for site, category in iterate_site_categories(): if site in sites: self.fail(f"Site {site} included more than once") sites.add(site) if __name__ == '__main__': unittest.main() ================================================ FILE: tests/test_sites_data.py ================================================ import unittest from whotracksme.data import DataSource class TestSitesData(unittest.TestCase): def test_all_sites_have_category(self): sites = DataSource().sites.get_snapshot() no_category_sites = list(filter(lambda s: s.category == '', sites)) self.assertEqual(no_category_sites, []) if __name__ == '__main__': unittest.main() ================================================ FILE: update_trackerdb.sh ================================================ #!/bin/bash # # Downloads the latest trackerdb release (from https://github.com/ghostery/trackerdb), # and updates "whotracksme/data/assets/trackerdb.sql". # make sure we are in the directory which contains the script cd "${BASH_SOURCE%/*}" latest_release=$(curl -s -I https://github.com/ghostery/trackerdb/releases/latest | awk '/^location/ { print $2 }' | tr -d '\r') if [[ $latest_release =~ ^https:\/\/github\.com\/ghostery\/trackerdb\/releases\/tag\/[0-9]+$ ]]; then echo "Latest release found: $latest_release" else echo "ERROR: Failed to find the latest release." exit 1 fi version="${latest_release##*/}" trackerdb_link="https://github.com/ghostery/trackerdb/releases/download/${version}/trackerdb.db" tmp=$(mktemp) tmp2=$(mktemp) cleanup() { rm -f "$tmp" rm -f "$tmp2" } trap cleanup EXIT echo "Downloading file from: ${trackerdb_link}" curl -s -L -o "$tmp" "$trackerdb_link" target=whotracksme/data/assets/trackerdb.sql sqlite3 "$tmp" .dump > "$tmp2" (echo "-- Generated from $trackerdb_link" ; cat "$tmp2") > "$target" echo "trackerdb.sql was successfully generated: $target" ================================================ FILE: update_trackers_preview.py ================================================ """ Helper script to update the trackers-preview.json file. The raw input data (aka "privacy score") is computed each month but is internal to the processing pipeline. This scripts downloads the data, slightly converts it to allow us to publish it. Usage: update_tracker_preview [INPUT] Args: FILE: optional local filename (by default, the data is loaded from S3) """ # To run the unit tests: # $ python -m doctest -v update_trackers_preview.py import os from docopt import docopt import boto3 import re import io import json def download_privacy_score(bucket_name, bucket_prefix): """ Find the latest month, download it and return its content as a dictionary. """ s3_client = boto3.client('s3') s3_client.get_paginator('list_objects_v2') input_matcher = re.compile(f'^{bucket_prefix}2[0-9][0-9][0-9]-[0-9][0-9][.]json$') def iterate_bucket(s3_client, bucket_name, bucket_prefix, input_matcher): pageinator = s3_client.get_paginator('list_objects_v2') for page in pageinator.paginate(Bucket=bucket_name, Prefix=bucket_prefix): if page['KeyCount'] == 0: continue for item in page['Contents']: if input_matcher.match(item['Key']): yield item['Key'] latest_key = max(iterate_bucket(s3_client, bucket_name, bucket_prefix, input_matcher)) print(f'Downloading latest_key file s3://{bucket_name}/{latest_key} ...') return json.loads(s3_client.get_object(Bucket=bucket_name, Key=latest_key)['Body'].read()) def list_known_categories(privacy_score): categories = set() for xs in (list(val['categories'].keys()) for val in privacy_score.values()): categories.update(xs) return categories def generate_trackers_preview(privacy_score): """ Generates a trackers_preview representation. >>> generate_trackers_preview(dict()) == { "categories": [], "trackers": {} } True >>> generate_trackers_preview({ ... "google.com": { ... "site": "google.com", ... "some_random_key_to_be_ignored": "...", ... "categories": { ... "cdn": 5, ... "audio_video_player": 1, ... "site_analytics": 1, ... "advertising": 2, ... "extensions": 1, ... "essential": 1, ... "misc": 1, ... "social_media": 1 ... } ... }, ... "youtube.com": { ... "site": "youtube.com", ... "some_other_key_to_be_ignored": "...", ... "categories": { ... "audio_video_player": 1, ... "cdn": 5, ... "advertising": 3, ... "site_analytics": 2, ... "social_media": 1, ... "extensions": 1, ... "essential": 1 ... } ... } ... }) == { ... "categories": ["advertising", "audio_video_player", "cdn", "essential", "extensions", "misc", "site_analytics", "social_media"], ... "trackers": { ... "google.com": [2, 1, 5, 1, 1, 1, 1, 1], ... "youtube.com": [3, 1, 5, 1, 1, 0, 2, 1] ... } ... } True """ sorted_categories = sorted(list_known_categories(privacy_score)) trackers = dict() for entry in privacy_score.values(): trackers[entry['site']] = [entry['categories'].get(cat, 0) for cat in sorted_categories] return { 'trackers': trackers, 'categories': sorted_categories } def write_json(data, path): with open(path, 'w') as f: f.write(json.dumps(data, separators=(',', ':'))) print(f'Successfully generated file: {path}') if __name__ == "__main__": args = docopt(__doc__) local_file = args['INPUT'] if local_file: print(f'Loading local file: {local_file} ...') privacy_score = json.loads(open(local_file, mode='r').read()) else: bucket_name = 'ghostery-antitracking-data' bucket_prefix = 'privacy-score/site/' privacy_score = download_privacy_score(bucket_name, bucket_prefix) trackers_preview = generate_trackers_preview(privacy_score) write_json(trackers_preview, 'whotracksme/data/assets/trackers-preview.json') ================================================ FILE: whotracksme/__init__.py ================================================ ================================================ FILE: whotracksme/data/Readme.md ================================================ # Data The data for the whotracks.me site is provided here as JSON files, with a SQL database containing tracker information. This document describes the format of the data provided in the `assets` directory. > Note: Beside the monthly data dump, there is also a separate project for the meta data. It is an open source project called [TrackerDB](https://github.com/ghostery/trackerdb). ## How to get the data You have two options to work with the raw data: 1. Explore the raw data from last month [through the web site](https://www.ghostery.com/whotracksme/explorer) 1. Download the data locally (including historic data) ### Use the Explorer on the whotracks.me website The last month can be directly accessed from website at https://www.ghostery.com/whotracksme/explorer > Note: The meaning of the column in the explorer in explained in this document (see below). ### Download raw data You can also download the datasets from the publicly available Amazon S3 bucket. First, you need to install the AWS CLI client: - [Linux](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-linux.html) - [MacOS](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-mac.html) - [Windows](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-windows.html) > Note: When building the website, the data is expected to be in `/whotracksme/data/assets` folder. To download one month of data (e.g. October 2022): ```sh cd /data/assets aws s3 sync --no-sign-request s3://data.whotracks.me/2022-10 . ``` You can also download historic data (back to `2017-05`)): ```sh cd /data/assets aws s3 sync --no-sign-request s3://data.whotracks.me/ . ``` ## Tracker database Generally, it is recommend to get the Tracker database directly from the upstream project: https://github.com/ghostery/trackerdb For consistency, a snapshot of the tracker database used to generate the monthly data set is also provided in the `assets/trackerdb.sql` file. This is a dump of a SQLite3 database containing the following tables: * `categories`: Categories for trackers (e.g. `advertising`, `social_media`). * `companies`: Metadata on companies: name, description, and various links. * `trackers`: Metadata on trackers: name, description, category, website and an optional link to a parent company. * `tracker_domains`: Table linking trackers to domain names. ## WhoTracks.me datasets WhoTracks.me datasets are provided monthly in the `assets//{month}/{country}/{file}.csv` format. A glimpse of the datasets is available in [Explorer](https://www.ghostery.com/whotracksme/explorer) section on the website. ### Data collection Nowadays, the data comes exclusively from users of the [Ghostery Extension](https://github.com/ghostery/ghostery-extension/). Precise user counts are difficult due to the nature of the data collection; but it is estimated to be in the order a few million devices per month, spread all around the world, but mostly in Europe and the US. The methodology is builds on the concept of k-Anonymity and is described in paper [Tracking the Trackers](https://0x65.dev/static/docs/studies/TrackingTheTrackers.pdf). The WhoTracks.me monthly data sets are derived from the same data that also powers the anti-tracking protection in Ghostery; it is also described in [this blog post](https://www.0x65.dev/blog/2019-12-19/blocking-tracking-without-blocking-trackers.html). The code can be found [here](https://github.com/whotracksme/webextension-packages/tree/main/reporting/src/request). Before 2018, all traffic came from users of the Cliqz Browser and the Cliqz extension (now both discontinued). Around April 2018, Ghostery users started to contribute to the data set. This both increased the user base and made it more internation (Cliqz was mostly used in German speaking regions). Here are some historical information from the Cliqz side (to help understand data sets before 2020): * Data was collected from May 2017 from users that used Cliqz browser extension. In Feb 2018, 70% of the data came from German users according to [this](https://web.archive.org/web/20240121094157/https://whotracks.me/blog/update_feb_2018.html) blog post. Then in March 2018, users of Ghostery Firefox extension - and Ghostery extension available for other browsers (Safari, Chrome, Opera and Edge) from users that opted-in to HumanWeb data collection - were added to the dataset. This caused a slight decrease in the avg. no. of trackers in April 2018, since Ghostery users were blocking more trackers. This is explained in [this](https://web.archive.org/web/20240430053538/https://whotracks.me/blog/where_is_the_data_from.html) and [this](https://web.archive.org/web/20240121094145/https://whotracks.me/blog/update_apr_2018.html) blog posts. * [This](https://web.archive.org/web/20240121094145/https://whotracks.me/blog/update_apr_2018.html) blog post illustrates where the traffic came from in April 2018: Germany and USA being most representative. * [This](https://web.archive.org/web/20241105085238/https://cliqz.com/en/magazine/government-websites-leak-data-to-google-co) blog post notes that WhoTracks.me does not collect data for pages with no trackers; in other words, collected data for all sites contains some number of third-parties and tracking. ### Datasets There are five main datasets (unlike the four datasets available in the [explorer](https://www.ghostery.com/whotracksme/explorer) section on the website): * `sites.csv`: Stats for number of trackers seen on popular websites. * `sites_trackers.csv`: Stats for each tracker on each site. * `domains.csv`: Top third-party domains seen tracking. * `trackers.csv`: Top trackers - this combines domains known be operated by the same tracker. * `companies.csv`: Top companies - aggregates the stats for trackers owned by the same company. > "We structure each subsection in a way that describes measurements in the perspective of the parties involved: websites, third parties and users. This enables us to better measure the dynamics of the industry." > Note on coverage: every CSV's per-page metrics (`requests`, `cookies`, etc.) are aggregations over third-party activity, regardless of how the rows are keyed. What differs is *which third-party activity is included*. `sites.csv` (keyed on first-party sites) and `domains.csv` (keyed on third-party domains) aggregate over all third parties observed, classified or not. `trackers.csv`, `companies.csv`, and `sites_trackers.csv` only include third parties that have an entry in [TrackerDB](https://github.com/ghostery/trackerdb). As a result, summing per-tracker totals up to a site will systematically undercount the matching `sites.csv` value — the gap is requests to unclassified third parties (e.g. CDNs, fonts, payment providers, smaller domains not in TrackerDB). ### Variable descriptions The data is created by aggregating data about page loads at several different levels. Therefore, all five above datasets share similar aggregated variables. The difference therefore, lies in the *perspective* of each dataset. Variable descriptions ("contexts" are added to variables for groupings) are given below: **General context**: * `site` - one of the most frequently visited websites from a proportion of traffic in certain month. That means that the most popular websites in the dataset were not generated by Alexa or similar services, but by real users. Total number of published most-visited-user-generated sites increased over time: e.g. from 700 most visited sites in 2017 to over 1000 sites in 2020 in Global datasets. Note: average monthly traffic (page loads) of users was around 100 million page loads during 2017, and increased to 300-500 million page loads from April 2018 onward (as described in [this](https://arxiv.org/pdf/1804.08959.pdf#page=2) part of WhoTracks.me paper). String. * `month` - month of observation. Global traffic data starts from May 2017 and ends with latest GitHub release; EU/US traffic split starts from April 2018 and ends with latest GitHub release. mm-yyyy format string/date. * `country` - main region where the traffic is coming from: e.g. global, US, EU, DE, FR. String. * `category` - site's category (in `sites.csv`). Descriptions of website categories (first-parties) are provided [here](https://arxiv.org/pdf/1804.08959v1.pdf#page=14) in Appendix A. String. * `tracker_category` - tracker's category (in `sites_trackers.csv`). Descriptions of tracker categories are provided [here](https://github.com/ghostery/trackerdb/blob/main/docs/categories.md). String. * `popularity` - the relative amount of traffic compared to the most popular site (described [here](https://web.archive.org/web/20240121094155/https://whotracks.me/blog/updating_our_tracking_prevalence_metrics.html)). Float between 0 and 1. **Utilised tracking context (stateful)** – generates more persistant tracking ID by trackers: * `cookies` - proportion of pages where a cookie was sent by the browser, or a `Set-Cookie` header was returned by the tracker's server. Float between 0 and 1. **Utilised tracking context (stateless)** – generates less persistant tracking ID by trackers: * `bad_qs` - proportion of pages where a unique identifier (UID) was detected in the query string parameters sent with a request to this tracker. More on this [here](https://en.wikipedia.org/wiki/Query_string#Tracking). The methodology for this detection can be found in [the paper](https://www.researchgate.net/publication/312638031_Tracking_the_Trackers). Float between 0 and 1. > "Note that these detection methods assume that trackers are not obfuscating the identifiers they generate." **Utilised tracking context (stateful + stateless)** – either cookie tracking or fingerprinting context, inclusive: * `tracked` - proportion of pages where a UID transmission was detected, either via `cookies` or `bad_qs`. Float between 0 and 1. > "We define tracking as when a service is able to collect and correlate data across multiple sites." **Secure context** – tracker used HTTPS requests - instead of HTTP - to load its content: * `https` - proportion of pages where the tracker only used `HTTPS` traffic. Float between 0 and 1. **Tracking requests context** – we report the mean number of third-party requests per page for each tracker, and the subset of these requests in a tracking context: * `requests` - average number of requests made to the tracker per page. Positive float. * `requests_tracking` - average number of requests made to the tracker with tracking (cookie or query string) per page. Positive float. **Tracking cost context** - how much page loads do trackers clog up by being loaded, more on this in the [Tracker Tax paper](https://web.archive.org/web/20180711021626/https://www.ghostery.com/wp-content/themes/ghostery/images/campaigns/tracker-tax/Ghostery_Study_-_The_Tracker_Tax.pdf): * `content_length` - average of `Content-Length` headers received per page. This is an approximate measure of the bandwidth usage of the tracker. Expressed in bytes. Positive float. > "As users navigate the web, they load content from websites they visit as well as the third parties present on the website. ... Previous studies have found that each extra third party added to the site will contribute to an increase of 2.5% in the site’s loading time." **Tracker's blocking context** – how often the tracker is affected by blocklist-based blockers: * `requests_failed` - average number of requests make to the tracker per page which do not succeed. In other words, avg. number of failed requests per page load (for comparison with `requests` to get an idea of how aggressive the blocking is). This is an approximate measure of blocking from external sources (i.e. adblocking extensions or firewalls). Measure [added](https://web.archive.org/web/20240121094211/https://whotracks.me/blog/update_dec_2017.html) in Dec 2017. Positive float. * `has_blocking` - proportion of pages where some kind of external blocking of the tracker was detected. Measure [added](https://web.archive.org/web/20240121094211/https://whotracks.me/blog/update_dec_2017.html) in Dec 2017. Float between 0 and 1. > "These signals [`requests_failed` and `has_blocking`] should be able to tell us something about the impact of blocking on different trackers in the ecosystem. For example, we see evidence of blocking 40% of the time for Google Analytics and Facebook [in Dec 2017], and between 10% and 20% of requests failing. Thus, anyone using these services to measure activity and conversions on their sites must reckon with error rates in these orders. We also can see how new entrants can initially avoid the effects of blocking - for Tru Optik and Digitrust who we mentioned earlier, we measure only 5 and 1% of pages which may be affected by blocking." **Tracker's content loading context** – proportion of page loads where specific resource types were loaded by the tracker (e.g. scripts, iframes, plugins) Signals for the frequency with which certain resource types are loaded by third-parties (measures [added](https://web.archive.org/web/20240121094157/https://whotracks.me/blog/update_feb_2018.html) in Feb 2018): * `script` - JavaScript code (via a `