From fe5c31a88f690d9c7decebf9a1ca5128ec76b6bd Mon Sep 17 00:00:00 2001 From: develop-cs <43383361+develop-cs@users.noreply.github.com> Date: Fri, 7 Jun 2024 10:51:51 +0200 Subject: [PATCH] Feature/prepare_release (#15) * docs: refactor documentation #3 Signed-off-by: develop-cs <43383361+develop-cs@users.noreply.github.com> * docs: add project page Signed-off-by: develop-cs <43383361+develop-cs@users.noreply.github.com> * docs: refactor the doc Signed-off-by: develop-cs <43383361+develop-cs@users.noreply.github.com> * docs: add arta logos Signed-off-by: develop-cs <43383361+develop-cs@users.noreply.github.com> * fix: fix typos Signed-off-by: develop-cs <43383361+develop-cs@users.noreply.github.com> * chore: use built-in type hints Signed-off-by: develop-cs <43383361+develop-cs@users.noreply.github.com> * docs: change package description Signed-off-by: develop-cs <43383361+develop-cs@users.noreply.github.com> * fix: some links Signed-off-by: develop-cs <43383361+develop-cs@users.noreply.github.com> * fix: typos Signed-off-by: develop-cs <43383361+develop-cs@users.noreply.github.com> --------- Signed-off-by: develop-cs <43383361+develop-cs@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/bug_report.md | 16 +- .github/ISSUE_TEMPLATE/feature_request.md | 8 +- .github/PULL_REQUEST_TEMPLATE.md | 6 +- .github/workflows/ci-cd-docs.yml | 3 +- CHANGELOG.md | 4 +- CONTRIBUTING.md | 4 +- README.md | 120 +- docs/mkdocs.yaml | 12 +- docs/pages/a_simple_example.md | 239 +++ docs/pages/assets/css/style.css | 343 +++ docs/pages/assets/img/arta-fond-clair.svg | 96 + docs/pages/assets/img/arta-git.svg | 82 + docs/pages/assets/img/etiquette.svg | 14 + docs/pages/assets/img/footer.svg | 237 +++ docs/pages/assets/js/tarteaucitron/LICENSE | 21 + docs/pages/assets/js/tarteaucitron/README.md | 101 + .../assets/js/tarteaucitron/advertising.js | 1 + .../js/tarteaucitron/css/tarteaucitron.css | 545 +++++ .../js/tarteaucitron/lang/tarteaucitron.cs.js | 65 + .../js/tarteaucitron/lang/tarteaucitron.de.js | 65 + .../js/tarteaucitron/lang/tarteaucitron.en.js | 65 + .../js/tarteaucitron/lang/tarteaucitron.es.js | 65 + .../js/tarteaucitron/lang/tarteaucitron.fr.js | 65 + .../js/tarteaucitron/lang/tarteaucitron.it.js | 65 + .../js/tarteaucitron/lang/tarteaucitron.pl.js | 66 + .../js/tarteaucitron/lang/tarteaucitron.pt.js | 61 + .../js/tarteaucitron/lang/tarteaucitron.ru.js | 65 + .../assets/js/tarteaucitron/tarteaucitron.js | 1265 +++++++++++ .../tarteaucitron/tarteaucitron.services.js | 1850 +++++++++++++++++ docs/pages/home.md | 30 + docs/pages/how_to.md | 502 +++-- docs/pages/in_a_nutshell.md | 199 -- docs/pages/index.html | 131 ++ docs/pages/index.md | 26 - docs/pages/installation.md | 2 +- docs/pages/parameters.md | 2 +- docs/pages/rule_sets.md | 4 +- docs/pages/special_conditions.md | 129 +- docs/pages/why.md | 6 +- pyproject.toml | 6 +- src/arta/_engine.py | 86 +- src/arta/condition.py | 22 +- src/arta/config.py | 12 +- src/arta/models.py | 5 +- src/arta/rule.py | 50 +- src/arta/utils.py | 14 +- tests/examples/code/actions.py | 10 +- tests/examples/code/conditions.py | 6 +- tests/examples/code/custom_class.py | 6 +- tests/test_example_code/test_actions.py | 2 +- 50 files changed, 6105 insertions(+), 694 deletions(-) create mode 100644 docs/pages/a_simple_example.md create mode 100644 docs/pages/assets/css/style.css create mode 100644 docs/pages/assets/img/arta-fond-clair.svg create mode 100644 docs/pages/assets/img/arta-git.svg create mode 100644 docs/pages/assets/img/etiquette.svg create mode 100644 docs/pages/assets/img/footer.svg create mode 100755 docs/pages/assets/js/tarteaucitron/LICENSE create mode 100755 docs/pages/assets/js/tarteaucitron/README.md create mode 100755 docs/pages/assets/js/tarteaucitron/advertising.js create mode 100755 docs/pages/assets/js/tarteaucitron/css/tarteaucitron.css create mode 100755 docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.cs.js create mode 100755 docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.de.js create mode 100755 docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.en.js create mode 100755 docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.es.js create mode 100755 docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.fr.js create mode 100755 docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.it.js create mode 100755 docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.pl.js create mode 100755 docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.pt.js create mode 100755 docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.ru.js create mode 100755 docs/pages/assets/js/tarteaucitron/tarteaucitron.js create mode 100755 docs/pages/assets/js/tarteaucitron/tarteaucitron.services.js create mode 100644 docs/pages/home.md delete mode 100644 docs/pages/in_a_nutshell.md create mode 100644 docs/pages/index.html delete mode 100644 docs/pages/index.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 55583d0..9e8de8c 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,25 +7,25 @@ assignees: '' --- -**Describe the bug** +**Describe the bug**: A clear and concise description of what the bug is. -**To Reproduce** +**To Reproduce**: Steps to reproduce the behavior: 1. Import '...' 2. Call '....' 3. See error -**Expected behavior** +**Expected behavior**: A clear and concise description of what you expected to happen. -**Screenshots** +**Screenshots**: If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - - OS: [e.g. linux] - - Python version [e.g. 3.12] - - Dependencies versions [e.g. pydantic 2.6.4] + - OS: `[e.g. linux]` + - Python version(s): `[e.g. 3.12]` + - Dependencies versions: `[e.g. pydantic 2.6.4]` -**Additional context** +**Additional context**: Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index e648b58..a52670b 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -7,14 +7,14 @@ assignees: '' --- -**Is your feature request related to a problem? Please describe** +**Is your feature request related to a problem? Please describe**: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -**Describe the solution you'd like** +**Describe the solution you'd like**: A clear and concise description of what you want to happen. -**Describe alternatives you've considered** +**Describe alternatives you've considered**: A clear and concise description of any alternative solutions or features you've considered. -**Additional context** +**Additional context**: Add any other context or screenshots about the feature request here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7c9f312..4ce9f62 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,11 +4,11 @@ Explain with one or more sentences what this change is doing. ## Have you done? - [ ] Code tests - [ ] Update documentation -- [ ] Update [changelog](https://github.com/MAIF/arta/blob/main/CHANGELOG.md). +- [ ] Update [changelog](https://github.com/MAIF/arta/blob/main/CHANGELOG.md) -## Details to be checked (optional) +## Details to be checked: (optional) If needed, add some details here in order to verify the change (e.g., how to test). -## Linked issues (optional) +## Linked issues: (optional) - Close ... - Close ... \ No newline at end of file diff --git a/.github/workflows/ci-cd-docs.yml b/.github/workflows/ci-cd-docs.yml index 6fbf9d8..dfa5e1e 100644 --- a/.github/workflows/ci-cd-docs.yml +++ b/.github/workflows/ci-cd-docs.yml @@ -16,7 +16,7 @@ on: jobs: build: if: ${{ github.actor != 'dependabot[bot]' }} - name: Check docs build + name: Build doc runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -37,6 +37,7 @@ jobs: path: "docs/site/" publish: if: success() && startsWith(github.ref, 'refs/tags') + name: Publish doc needs: build permissions: pages: write diff --git a/CHANGELOG.md b/CHANGELOG.md index 9842d00..c016135 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.7.0] - April, 2024 +## [0.7.0b] - April, 2024 ### New Features -- Beta release implementing what you can find in its [documentation](https://pages.github.com/MAIF/arta). +- Beta release implementing what you can find in its [documentation](https://pages.github.com/MAIF/arta/home). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 398186f..601ed7d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,7 +7,7 @@ We encourage changes that make it easier to achieve our goals in an efficient wa ## Codebase -explain the layout of your repo. +Explain the layout of your repo. ## Workflow @@ -22,7 +22,7 @@ We follow the standard GitHub [fork & pull](https://help.github.com/articles/usi - Please make sure to follow the general quality guidelines (specified below) when developing your patch. - Please write additional tests covering your feature and adjust existing ones if needed before submitting your pull request. 1. Once your feature is complete, prepare the commit with a good commit message, for example: `Adding canary mode support for services #42` (note the reference to the ticket it aimed to resolve). -1. If it's a new feature, or a change of behaviour, document it on the [Arta docs](https://github.com/MAIF/arta/tree/master/manual), remember, an undocumented feature is not a feature. +1. If it's a new feature, or a change of behaviour, document it on the [Arta docs](https://pages.github.com/MAIF/arta/home), remember, an undocumented feature is not a feature. 1. Now it's finally time to [submit the pull request](https://help.github.com/articles/using-pull-requests)! - Please make sure to include a reference to the issue you're solving *in the comment* for the Pull Request, this will cause the PR to be linked properly with the Issue. Examples of good phrases for this are: "Resolves #1234" or "Refs #1234". 1. Now both committers and interested people will review your code. This process is to ensure the code we merge is of the best possible quality, and that no silly mistakes slip through. You're expected to follow-up these comments by adding new commits to the same branch. The commit messages of those commits can be more loose, for example: `Removed debugging using printline`, as they all will be squashed into one commit before merging into the main branch. diff --git a/README.md b/README.md index 3b4c110..da04048 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,122 @@ -# Arta +

+ Arta +

+

+ Make rule handling simple +

+

+ CI + Coverage + Versions + Python + Downloads +

-![Alt CI](https://github.com/MAIF/arta/actions/workflows/ci-cd.yml/badge.svg?branch=main) -![Alt Python](https://img.shields.io/badge/Python-3.9_--_3.12-blue) +--- -[![Alt MAIF Logo](https://static.maif.fr/resources/img/logo-maif.svg)](https://www.maif.fr/) +**Documentation:** [https://pages.github.com/MAIF/arta/home](https://pages.github.com/MAIF/arta/home) + +**Repository:** [https://github.com/MAIF/arta](https://github.com/MAIF/arta) + +--- + +## Overview + +**Arta** is a simple python rules engine designed for python developers. + +### Goal + +There is one main reason for using **Arta** and it was the main goal of its development at *MAIF*: increase business rules maintainability. + +In other words, facilitate rules handling in our python apps. + +### Origins + +The need of a python *rules engine* emerged when we were working on a new major release of our internal use of [Melusine](https://github.com/maif/melusine) (i.e., email qualification pipeline with ML capabilities). + +We were looking for a python library to *centralize, manage and standardize* all the implemented **business rules** we had but didn't find the perfect fit. + +Therefore, we decided to create this package and by extension of the MAIF's values, we planned to share it to the community. + +### Features + +* Standardize the definition of a rule. All rules are defined the same way in a unique place. +* Rules are released from the code base, which is less error prone and increases clearness. +* Use **Arta** whatever your field is. +* Great combination with Machine Learning: groups all the deterministic rules of your ML projects. + +### A Simple Example + +Create the three following files and run the `main.py` script (`python main.py` or `python3 main.py`). -**Arta** is a very simple python rules engine designed for and by python developers. +`rules.yaml` : -## Reference Documentation +```yaml +--- +rules: + default_rule_set: + admission: + ADMITTED: + simple_condition: input.power=="strength" or input.power=="fly" + action: set_admission + action_parameters: + value: True + NOT_ADMITTED: + simple_condition: null + action: set_admission + action_parameters: + value: False -The reference documentation is available [here](https://solid-eureka-93ze9kn.pages.github.io/). +actions_source_modules: + - actions +``` + +`actions.py` : + +```python +def set_admission(value: bool, **kwargs: Any) -> dict[str, bool]: + """Return a dictionary containing the admission result.""" + return {"is_admitted": value} +``` + +`main.py` : + +```python +from arta import RulesEngine + +eng = RulesEngine(config_path=".") + +data = { + "id": 1, + "name": "Superman", + "civilian_name": "Clark Kent", + "age": None, + "city": "Metropolis", + "language": "english", + "power": "fly", + "favorite_meal": "Spinach", + "secret_weakness": "Kryptonite", + } + +result = eng.apply_rules(input_data=data) + +print(result) +``` + +You should get: `{"admission": {"is_admitted": True}}` + +Check the [A Simple Example](https://pages.github.com/MAIF/arta/a_simple_example/) section for more details. + +## Installation + +Install using `pip install -U arta`. See the [Install](https://pages.github.com/MAIF/arta/installation/) section in the documentation for more details. ## What's New Want to see last updates, check the [Release Notes](https://github.com/MAIF/arta/releases) or the [Changelog](./CHANGELOG.md). ## Community + You can discuss and ask *Arta* related questions: - Issue tracker: [![github: MAIF/arta/issues](https://img.shields.io/github/issues/MAIF/arta.svg)](https://github.com/MAIF/arta/issues) @@ -32,4 +133,7 @@ and general hints on how to prepare your pull request. You can also ask for clar ## License -This project is Open Source and available under the Apache 2 License. \ No newline at end of file +This project is Open Source and available under the Apache 2 License. + +[![Alt MAIF Logo](https://static.maif.fr/resources/img/logo-maif.svg)](https://www.maif.fr/) + diff --git a/docs/mkdocs.yaml b/docs/mkdocs.yaml index b99d3f1..e03c30a 100644 --- a/docs/mkdocs.yaml +++ b/docs/mkdocs.yaml @@ -1,16 +1,16 @@ site_name: Arta -site_url: https://pages.github.com/MAIF/arta +site_url: https://maif.github.io/arta repo_url: https://github.com/MAIF/arta repo_name: MAIF/arta site_author: OSSbyMAIF Team docs_dir: pages theme: name: 'material' - logo: https://static.maif.fr/resources/img/logo-maif.svg - favicon: https://static.maif.fr/resources/img/logo-maif.svg + logo: assets/img/arta-fond-clair.svg + favicon: assets/img/arta-git.svg palette: primary: white - accent: light green + accent: red font: text: 'Roboto' code: 'Roboto Mono' @@ -51,9 +51,9 @@ plugins: - mkdocstrings - search nav: - - Home: index.md + - Home: home.md - Get Started: - - In a nutshell: in_a_nutshell.md + - A Simple Example: a_simple_example.md - Installation: installation.md - Why use Arta?: why.md - User Guide: diff --git a/docs/pages/a_simple_example.md b/docs/pages/a_simple_example.md new file mode 100644 index 0000000..a063be4 --- /dev/null +++ b/docs/pages/a_simple_example.md @@ -0,0 +1,239 @@ +## Intro + +As we already mentioned: ***Arta** is a simple python rules engine*. + +But what do we mean by *rules engine*? + +* **rule** : a set of different conditions that can be `True` or `False` (i.e., we say *verified* or *not verified*) triggering an action (i.e., any python callable object). +* **engine** : some code used for combining and evaluating different rules on some input data. + +## The Superhero School :school: :superhero: + +Imagine the following use case: + +*Your are managing a superhero school and you want to use some school rules in your python app.* + +The rules (intentionally simple) are: + +!!! success "Admission rules" + + If the applicant has a school authorized power then he is admitted, + + Else he is not. + +!!! example "Course selection rules" + + If he is speaking french and his age is known then he must take the "french" course, + + Else if his age is unknown (e.g., it's a very old superhero), then he must take the "senior" course, + + Else if he is not speaking french, then he must take the "international" course. + +!!! info "Send favorite meal rules" + + If he is admitted and has a prefered dish, then we send an email to the school cook with the dish name. + +### Rules + +You can define above rules for **Arta** in one simple *YAML* file : + +```yaml +--- +rules: + default_rule_set: + admission: + ADMITTED: + simple_condition: input.power=="strength" or input.power=="fly" + action: set_admission + action_parameters: + value: True + NOT_ADMITTED: + simple_condition: null + action: set_admission + action_parameters: + value: False + course: + FRENCH: + simple_condition: input.language=="french" and input.age!=None + action: set_course + action_parameters: + value: french + SENIOR: + simple_condition: input.age==None + action: set_course + action_parameters: + value: senior + INTERNATIONAL: + simple_condition: input.language!="french" + action: set_course + action_parameters: + value: international + favorite_meal: + EMAIL: + simple_condition: input.favorite_meal!=None + action: send_email + action_parameters: + mail_to: cook@super-heroes.test + mail_content: "Thanks for preparing once a month the following dish:" + meal: input.favorite_meal + +actions_source_modules: + - my_folder.actions +``` + +!!! info "Simple Conditions" + + This configuration uses what we called *simple conditions*, you can find out more [here](how_to.md#simple-condition). + +### Actions + +An **action** is triggered when the conditions are verified (i.e., `True`). + +**Actions** are defined by the following *keys* in the previous YAML file: + +```yaml + action: set_admission # (1) + action_parameters: # (2) + value: True +``` + +1. Name of the *python callable object* used as an **action function**. +2. The **action function** arguments. + +The **action function**'s implementation has to be located in the configured module: + +```yaml +actions_source_modules: + - my_folder.actions +``` + +And could be for example (intentionally simple) in `actions.py`: + +```python +def set_admission(value: bool, **kwargs: Any) -> dict[str, bool]: + """Return a dictionary containing the admission result.""" + return {"is_admitted": value} + + +def set_course(course_id: str, **kwargs: Any) -> dict[str, str]: + """Return the course id as a dictionary.""" + return {"course_id": course_id} + + +def send_email(mail_to: str, mail_content: str, meal: str, **kwargs: Any) -> bool: + """Send an email.""" + result: str | None = None + + if meal is not None: + # API call here + result = "sent" + + return result +``` + +!!! warning "\*\*kwargs" + + **\*\*kwargs** is mandatory in *action functions*. + +### Engine + +The *rules engine* is responsible for evaluating the [configured rules](#rules-definition) against some *data* (usually named *"input data"*). + +In our use case, the input data could be a list of applicants: + +```python +applicants = [ + { + "id": 1, + "name": "Superman", + "civilian_name": "Clark Kent", + "age": None, + "city": "Metropolis", + "language": "english", + "power": "fly", + "favorite_meal": "Spinach", + "secret_weakness": "Kryptonite", + "weapons": [], + }, + { + "id": 2, + "name": "Batman", + "civilian_name": "Bruce Wayne", + "age": 33, + "city": "Gotham City", + "language": "english", + "power": "strength", + "favorite_meal": None, + "secret_weakness": "Feel alone", + "weapons": ["Hands", "Batarang"], + }, + { + "id": 3, + "name": "Wonder Woman", + "civilian_name": "Diana Prince", + "age": 5000, + "city": "Island of Themyscira", + "language": "french", + "power": "strength", + "favorite_meal": None, + "secret_weakness": "Lost faith in humanity", + "weapons": ["Magic lasso", "Bulletproof bracelets", "Sword", "Shield"], + }, +] +``` + +Now, let's apply the **rules** on a single applicant: + +```python +from arta import RulesEngine + +eng = RulesEngine(config_path="/to/my/config/dir") # (1) + +result = eng.apply_rules(input_data=applicants[0]) + +print(result) # (2) +# { +# "admission": {"is_admitted": True}, +# "course": {"course_id": "senior"}, +# "favorite_meal": "sent" +# } +``` + +1. Many possibilites for instanciation, we will explain them later. +2. Print a single result for the first applicant. + +In the **rules engine** result, we have 3 outputs: + +* `"admission": {"is_admitted": True},` +* `"course": {"course_id": "senior"},` +* `"favorite_meal": "sent"` + +Each corresponds to one of these [rules](#quick-example-the-superhero-school). + +Here, we can apply the rules to all the **data set** (3 applicants) with a simple dictionary comprehension: + +```python +from arta import RulesEngine + +results = {applicant["name"]: eng.apply_rules(applicant) for applicant in applicants} + +print(results) +# { +# "Superman": { +# "admission": {"is_admitted": True}, +# "course": {"course_id": "senior"}, +# "favorite_meal": "sent"}, +# "Batman": { +# "admission": {"is_admitted": True}, +# "course": {"course_id": "international"}, +# "favorite_meal": None, +# }, +# "Wonder Woman": { +# "admission": {"is_admitted": True}, +# "course": {"course_id": "french"}, +# "favorite_meal": None, +# } +# } +``` + +It is the end of this **Arta**'s overview. If you want now to go deeper in how to use **Arta**, click [here](how_to.md). \ No newline at end of file diff --git a/docs/pages/assets/css/style.css b/docs/pages/assets/css/style.css new file mode 100644 index 0000000..c3acb8f --- /dev/null +++ b/docs/pages/assets/css/style.css @@ -0,0 +1,343 @@ +body { + font-family: 'Arvo', serif; + background-color: #2E3031; +} + +.otoColor { + background-color: #CF363B; +} + +a { + color: #FFF; + text-decoration: underline; +} + +a:hover { + color: #CF363B; +} + +.home { + position: fixed; + z-index: 1001; + margin-left: 10px; + margin-top: 10px; +} + +.home a { + color: #2E3031; +} + +.home a:hover { + color: #af8900; +} + +.tagN2 { + width: 20%; + display: block; + margin: 20px auto; +} + +.contentN2 { + padding-top: 30px; +} + +.contentN2 .col-md-6 { + padding-top: 70px; +} + +.colDashRight { + border-right: 1px dashed #F2F2F2; + padding-right: 60px; +} + +.titleOnLine { + color: #fff; + font-size: 36px; + text-transform: uppercase; + padding-left: 0px; +} + +.titleOnLine li { + list-style-type: none; +} + +.titleOnLine li:nth-child(even) { + color: #CF363B; +} + +h1 { + font-size: 24px; +} + +h2 { + color: #fff; + font-size: 21px; + text-transform: uppercase; +} + +.manifesto { + border: 0; + position: fixed; + right: 0; + top: 0; + z-index: 9000; +} + +.intro { + font-family: 'Roboto', serif; + font-size: 18px; + color: #fff; +} + +.details { + font-family: 'Roboto', serif; + font-size: 14px; + font-weight: 300; + color: #fff; + margin-top: 40px; +} + +.details ul { + padding-left: 20px +} + +.more-details { + text-align: center; + color: #fff; + padding-left: 60px; +} + +.more-details .pictoPortrait { + height: 50px; +} + +.youtube_player { + margin-bottom: 20px; +} + +.youtube_player { + position: relative; + padding-bottom: 56.25%; + padding-top: 30px; + height: 0; + overflow: hidden; +} + +.youtube_player iframe, +.youtube_player object, +.youtube_player embed { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.youtube_player .tac_activate { + background: #2E3031; +} + +.more-details .pictoPaysage { + margin-top: 20px; + width: 70px +} + +.outer-circle { + height: 100px; + width: 100px; + position: relative; + border: 3px solid #CF363B; + border-radius: 50%; + margin: auto; + margin-top: 50px; +} + +.inner-circle { + position: absolute; + background: #CF363B; + border-radius: 50%; + height: 90px; + width: 90px; + top: 50%; + left: 50%; + margin: -45px 0px 0px -45px; + color: #2E3031; +} + +.inner-circle p { + margin-top: 40%; +} + +.inner-circle--lowerText { + font-size: 13px; +} + +.sidebar { + background: linear-gradient(90deg, #CF363B 50%, #2E3031 50%); + position: fixed; + top: 0; + bottom: 0; + left: 0; + z-index: 1000; + display: block; + overflow-x: hidden; + overflow-y: auto; + border-right: none; +} + +.sidebar-container { + display: flex; + height: 100%; + flex-direction: column; + justify-content: space-between; +} + +.sidebar-content { + flex-grow: 1; + padding-top: 20px; +} + +.footer-content { + width: 300%; + margin-left: -100%; + overflow: hidden; + bottom: 0; +} + +.footer-contentN2 { + position: relative; + width: 50%; + z-index: 1000; + margin-left: -5%; +} + +@media screen and (max-width: 991px) { + .intro { + font-size: 16px; + } + + .colDashRight { + border: none; + padding-right: 15px; + } + + .titleOnLine { + font-size: 26px; + } + + .titleOnLine li { + text-align: center; + } + + .details li, + .details { + list-style-type: none; + text-align: center; + } + + .more-details { + padding-left: 15px; + } +} + +@media screen and (max-width: 770px) { + .home { + position: absolute; + } + + .home .fa-home { + font-size: 1.5em; + } + + .sidebar-content { + width: 200px; + padding-left: 30px; + } + + .contentN2 { + padding-left: 30px; + } + + .sidebar-container { + flex-direction: column; + } + + .sidebar { + background: linear-gradient(180deg, #CF363B 50%, #2E3031 50%); + position: inherit; + } + + .footer-contentN2 { + margin-left: 0px; + margin-top: 20px; + width: 100%; + background-color: #CF363B; + } + + .intro { + text-align: left; + } + + .titleOnLine { + text-align: left; + } + + .titleOnLine li { + display: inline-block; + } + + .more-details { + text-align: left; + } + + .contentCircle { + margin-right: 20px; + } + + .tagN2 { + margin-top: 10px; + } + + .titleOnLine li { + text-align: left; + } + + .details li, + .details { + text-align: left; + } + + .details ul { + padding-left: 0px; + } + + .outer-circle { + margin-left: 0px; + text-align: center; + } + + .linkN2 { + padding-left: 20px; + } +} + +@media screen and (max-width: 400px) { + .outer-circle { + height: 80px; + width: 80px; + } + + .inner-circle { + height: 70px; + width: 70px; + margin: -35px 0px 0px -35px; + } + + .inner-circle p { + font-size: 12px; + } + + .inner-circle p.inner-circle--lowerText { + font-size: 10px; + } +} \ No newline at end of file diff --git a/docs/pages/assets/img/arta-fond-clair.svg b/docs/pages/assets/img/arta-fond-clair.svg new file mode 100644 index 0000000..89ad589 --- /dev/null +++ b/docs/pages/assets/img/arta-fond-clair.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/pages/assets/img/arta-git.svg b/docs/pages/assets/img/arta-git.svg new file mode 100644 index 0000000..b8d47fb --- /dev/null +++ b/docs/pages/assets/img/arta-git.svg @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/pages/assets/img/etiquette.svg b/docs/pages/assets/img/etiquette.svg new file mode 100644 index 0000000..608bbbc --- /dev/null +++ b/docs/pages/assets/img/etiquette.svg @@ -0,0 +1,14 @@ + + + + + + + + + + Python + + + + diff --git a/docs/pages/assets/img/footer.svg b/docs/pages/assets/img/footer.svg new file mode 100644 index 0000000..0a9fab1 --- /dev/null +++ b/docs/pages/assets/img/footer.svg @@ -0,0 +1,237 @@ + + + + + + Plan de travail 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/pages/assets/js/tarteaucitron/LICENSE b/docs/pages/assets/js/tarteaucitron/LICENSE new file mode 100755 index 0000000..adde226 --- /dev/null +++ b/docs/pages/assets/js/tarteaucitron/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 AmauriC + +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. diff --git a/docs/pages/assets/js/tarteaucitron/README.md b/docs/pages/assets/js/tarteaucitron/README.md new file mode 100755 index 0000000..2cbfb3a --- /dev/null +++ b/docs/pages/assets/js/tarteaucitron/README.md @@ -0,0 +1,101 @@ +[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/SASAICAGENCY) + + +tarteaucitron.js +================ +Comply to the european cookie law is simple with the french *tarte au citron*. + +# What is this script? +The european cookie law regulates the management of cookies and you should ask your visitors their consent before exposing them to third party services. + +Clearly this script will: +- Disable all services by default, +- Display a banner on the first page view and a small one on other pages, +- Display a panel to allow or deny each services one by one, +- Activate services on the second page view if not denied, +- Store the consent in a cookie for 365 days. + +Bonus: +- Load service when user click on Allow (without reload of the page), +- Incorporate a fallback system (display a link instead of social button and a static banner instead of advertising). + +## Supported services +* Advertising network + * Amazon + * Clicmanager + * Criteo + * FERank (pub) + * Google Adsense + * Google Adsense Search (form) + * Google Adsense Search (result) + * Google Adwords (conversion) + * Google Adwords (remarketing) + * Pubdirecte + * Twenga + * vShop + +* APIs + * Google jsapi + * Google Maps + * Google Tag Manager + * Timeline JS + * Typekit (adobe) + +* Audience measurement + * Alexa + * Clicky + * Crazyegg + * FERank + * Get+ + * Google Analytics (ga.js) + * Google Analytics (universal) + * StatCounter + * VisualRevenue + * Xiti + +* Comment + * Disqus + * Facebook (commentaire) + +* Social network + * AddThis + * AddToAny (feed) + * AddToAny (share) + * eKomi + * Facebook + * Facebook (like box) + * Google+ + * Google+ (badge) + * Linkedin + * Pinterest + * Shareaholic + * ShareThis + * Twitter + * Twitter (cards) + * Twitter (timelines) + +* Support + * UserVoice + * Zopim + +* Video + * Calameo + * Dailymotion + * Prezi + * SlideShare + * Vimeo + * YouTube + + +## Visitors outside the EU +In PHP for example, you can bypass all the script by setting this var `tarteaucitron.user.bypass = true;` if the visitor is not in the EU. + +## Tested on +- IE 6+ +- FF 3+ +- Safari 4+ +- Chrome 14+ +- Opera 10+ + +# Installation guide +[Visit opt-out.ferank.eu](https://opt-out.ferank.eu/) diff --git a/docs/pages/assets/js/tarteaucitron/advertising.js b/docs/pages/assets/js/tarteaucitron/advertising.js new file mode 100755 index 0000000..813e11b --- /dev/null +++ b/docs/pages/assets/js/tarteaucitron/advertising.js @@ -0,0 +1 @@ +tarteaucitronNoAdBlocker = true; \ No newline at end of file diff --git a/docs/pages/assets/js/tarteaucitron/css/tarteaucitron.css b/docs/pages/assets/js/tarteaucitron/css/tarteaucitron.css new file mode 100755 index 0000000..d0f24c8 --- /dev/null +++ b/docs/pages/assets/js/tarteaucitron/css/tarteaucitron.css @@ -0,0 +1,545 @@ +/*** + * Responsive layout for the control panel + */ +@media screen and (max-width:479px) { + #tarteaucitron .tarteaucitronLine .tarteaucitronName { + width: 90% !important; + } + + #tarteaucitron .tarteaucitronLine .tarteaucitronAsk { + float: left !important; + margin: 10px 15px 5px; + } +} + +@media screen and (max-width:767px) { + #tarteaucitronAlertSmall #tarteaucitronCookiesListContainer, #tarteaucitron { + background: #fff; + border: 0 !important; + bottom: 0 !important; + height: 100% !important; + left: 0 !important; + margin: 0 !important; + max-height: 100% !important; + max-width: 100% !important; + top: 0 !important; + width: 100% !important; + } + + #tarteaucitron .tarteaucitronBorder { + border: 0 !important; + } + + #tarteaucitronAlertSmall #tarteaucitronCookiesListContainer #tarteaucitronCookiesList { + border: 0 !important; + } + + #tarteaucitron #tarteaucitronServices .tarteaucitronTitle { + text-align: left !important; + } +} + +@media screen and (min-width:768px) and (max-width:991px) { + #tarteaucitron { + border: 0 !important; + left: 0 !important; + margin: 0 5% !important; + max-height: 80% !important; + width: 90% !important; + } +} + +/*** + * Common value + */ +#tarteaucitron * { + zoom: 1; +} + +#tarteaucitron .clear { + clear: both; +} + +#tarteaucitron a { + color: rgb(66, 66, 66); + font-size: 11px; + font-weight: 700; + text-decoration: none; +} + +#tarteaucitronAlertBig a, #tarteaucitronAlertSmall a { + color: #fff; +} + +#tarteaucitron b { + font-size: 22px; + font-weight: 500; +} + +/*** + * Root div added just before + */ +#tarteaucitronRoot { + left: 0; + position: absolute; + right: 0; + top: 0; + width: 100%; +} + +#tarteaucitronRoot * { + box-sizing: initial; + color: #333; + font-family: sans-serif !important; + font-size: 14px; + line-height: normal; + vertical-align: initial; +} + +/*** + * Control panel + */ +#tarteaucitronBack { + background: #fff; + display: none; + height: 100%; + left: 0; + opacity: 0.7; + position: fixed; + top: 0; + width: 100%; + z-index: 2147483646; +} + +#tarteaucitron { + display: none; + max-height: 80%; + left: 50%; + margin: 0 auto 0 -430px; + padding: 0; + position: fixed; + top: 6%; + width: 860px; + z-index: 2147483647; +} + +#tarteaucitron .tarteaucitronBorder { + background: #fff; + border: 2px solid #333; + border-top: 0; + height: auto; + overflow: auto; +} + +#tarteaucitronAlertSmall #tarteaucitronCookiesListContainer #tarteaucitronClosePanelCookie, +#tarteaucitron #tarteaucitronClosePanel { + background: #333333; + color: #fff; + cursor: pointer; + font-size: 12px; + font-weight: 700; + text-decoration: none; + padding: 4px 0; + position: absolute; + right: 0; + text-align: center; + width: 70px; +} + +#tarteaucitron #tarteaucitronDisclaimer { + color: #555; + font-size: 12px; + margin: 15px auto 0; + width: 80%; +} + +#tarteaucitronAlertSmall #tarteaucitronCookiesListContainer #tarteaucitronCookiesList .tarteaucitronHidden, +#tarteaucitron #tarteaucitronServices .tarteaucitronHidden { + background: rgba(51, 51, 51, 0.07); +} + +#tarteaucitron #tarteaucitronServices .tarteaucitronHidden { + display: none; + position: relative; +} + +#tarteaucitronAlertSmall #tarteaucitronCookiesListContainer #tarteaucitronCookiesList .tarteaucitronTitle, +#tarteaucitron #tarteaucitronServices .tarteaucitronTitle, +#tarteaucitron #tarteaucitronInfo, +#tarteaucitron #tarteaucitronServices .tarteaucitronDetails { + background: #333; + color: #fff; + display: inline-block; + font-size: 14px; + font-weight: 700; + margin: 20px 0px 0px; + padding: 5px 20px; + text-align: left; + width: auto; +} + +#tarteaucitron #tarteaucitronServices .tarteaucitronMainLine .tarteaucitronName a, +#tarteaucitron #tarteaucitronServices .tarteaucitronTitle a { + color: #fff; + font-weight: 500; +} + +#tarteaucitron #tarteaucitronServices .tarteaucitronMainLine .tarteaucitronName a:hover, +#tarteaucitron #tarteaucitronServices .tarteaucitronTitle a:hover { + text-decoration: none !important; +} + +#tarteaucitron #tarteaucitronServices .tarteaucitronMainLine .tarteaucitronName a { + font-size: 22px; +} + +#tarteaucitron #tarteaucitronServices .tarteaucitronTitle a { + font-size: 14px; +} + +#tarteaucitronAlertSmall #tarteaucitronCookiesListContainer #tarteaucitronCookiesList .tarteaucitronTitle { + padding: 5px 10px; +} + +#tarteaucitron #tarteaucitronInfo, +#tarteaucitron #tarteaucitronServices .tarteaucitronDetails { + color: #fff; + display: none; + font-size: 12px; + font-weight: 500; + margin-top: 0; + max-width: 270px; + padding: 20px; + position: absolute; + z-index: 2147483647; +} + +#tarteaucitron #tarteaucitronInfo a { + color: #fff; + text-decoration: underline; +} + +#tarteaucitron #tarteaucitronServices .tarteaucitronLine:hover { + background: rgba(51, 51, 51, 0.2); +} + +#tarteaucitron #tarteaucitronServices .tarteaucitronLine { + background: rgba(51, 51, 51, 0.1); + border-left: 5px solid transparent; + margin: 0; + overflow: hidden; + padding: 15px 5px; +} + +#tarteaucitron #tarteaucitronServices .tarteaucitronMainLine { + background: #333; + border: 3px solid #333; + border-left: 9px solid #333; + border-top: 5px solid #333; + margin-bottom: 0; + margin-top: 21px; + position: relative; +} + +#tarteaucitron #tarteaucitronServices .tarteaucitronMainLine:hover { + background: #333; +} + +#tarteaucitron #tarteaucitronServices .tarteaucitronMainLine .tarteaucitronName { + margin-left: 15px; + margin-top: 2px; +} + +#tarteaucitron #tarteaucitronServices .tarteaucitronMainLine .tarteaucitronName b { + color: #fff; +} + +#tarteaucitron #tarteaucitronServices .tarteaucitronMainLine .tarteaucitronAsk { + margin-top: 0px !important; +} + +#tarteaucitron #tarteaucitronServices .tarteaucitronLine .tarteaucitronName { + display: inline-block; + float: left; + margin-left: 10px; + text-align: left; + width: 50%; +} + +#tarteaucitron #tarteaucitronServices .tarteaucitronLine .tarteaucitronName a:hover { + text-decoration: underline; +} + +#tarteaucitron #tarteaucitronServices .tarteaucitronLine .tarteaucitronAsk { + display: inline-block; + float: right; + margin: 7px 15px 0; + text-align: right; +} + +#tarteaucitron #tarteaucitronServices .tarteaucitronLine .tarteaucitronAsk .tarteaucitronAllow, +#tarteaucitron #tarteaucitronServices .tarteaucitronLine .tarteaucitronAsk .tarteaucitronDeny, +.tac_activate .tarteaucitronAllow { + background: gray; + border-radius: 4px; + color: #fff; + cursor: pointer; + display: inline-block; + padding: 6px 10px; + text-align: center; + text-decoration: none; + width: auto; +} + +#tarteaucitron #tarteaucitronServices .tarteaucitronLine .tarteaucitronName .tarteaucitronListCookies { + color: #333; + font-size: 12px; +} + +/*** + * Big alert + */ +.tarteaucitronAlertBigTop { + top: 0; +} + +.tarteaucitronAlertBigBottom { + bottom: 0; +} + +#tarteaucitronAlertBig { + background: #333; + color: #fff; + display: none; + font-size: 15px !important; + left: 0; + padding: 5px 5%; + position: fixed; + text-align: center; + width: 90%; + box-sizing: content-box; + z-index: 2147483645; +} + +#tarteaucitronAlertBig #tarteaucitronDisclaimerAlert, +#tarteaucitronAlertBig #tarteaucitronDisclaimerAlert b { + font: 15px verdana; + color: #fff; +} + +#tarteaucitronAlertBig #tarteaucitronDisclaimerAlert b { + font-weight: 700; +} + +#tarteaucitronAlertBig #tarteaucitronCloseAlert, #tarteaucitronAlertBig #tarteaucitronPersonalize { + background: #008300; + color: #fff; + cursor: pointer; + display: inline-block; + font-size: 16px; + padding: 5px 10px; + text-decoration: none; + margin-left: 7px; +} + +#tarteaucitronAlertBig #tarteaucitronCloseAlert { + background: #fff; + color: #333; + font-size: 13px; + margin-bottom: 3px; + margin-left: 7px; + padding: 4px 10px; +} + +#tarteaucitronPercentage { + background: #0A0; + box-shadow: 0 0 2px #fff, 0 1px 2px #555; + height: 5px; + left: 0; + position: fixed; + width: 0; + z-index: 2147483644; +} + +/*** + * Small alert + */ +#tarteaucitronAlertSmall { + background: #333; + bottom: 0; + display: none; + padding: 0; + position: fixed; + right: 0; + text-align: center; + width: auto; + z-index: 2147483646; +} + +#tarteaucitronAlertSmall #tarteaucitronManager { + color: #fff; + cursor: pointer; + display: inline-block; + font-size: 11px !important; + padding: 8px 10px 8px; +} + +#tarteaucitronAlertSmall #tarteaucitronManager:hover { + background: rgba(255, 255, 255, 0.05); +} + +#tarteaucitronAlertSmall #tarteaucitronManager #tarteaucitronDot { + background-color: gray; + border-radius: 5px; + display: block; + height: 8px; + margin-bottom: 1px; + margin-top: 5px; + overflow: hidden; + width: 100%; +} + +#tarteaucitronAlertSmall #tarteaucitronManager #tarteaucitronDot #tarteaucitronDotGreen, +#tarteaucitronAlertSmall #tarteaucitronManager #tarteaucitronDot #tarteaucitronDotYellow, +#tarteaucitronAlertSmall #tarteaucitronManager #tarteaucitronDot #tarteaucitronDotRed { + display: block; + float: left; + height: 100%; + width: 0%; +} + +#tarteaucitronAlertSmall #tarteaucitronManager #tarteaucitronDot #tarteaucitronDotGreen { + background-color: #1B870B; +} + +#tarteaucitronAlertSmall #tarteaucitronManager #tarteaucitronDot #tarteaucitronDotYellow { + background-color: #FBDA26; +} + +#tarteaucitronAlertSmall #tarteaucitronManager #tarteaucitronDot #tarteaucitronDotRed { + background-color: #9C1A1A; +} + +#tarteaucitronAlertSmall #tarteaucitronCookiesNumber { + background: rgba(255, 255, 255, 0.2); + color: #fff; + cursor: pointer; + display: inline-block; + font-size: 30px; + padding: 0px 10px; + vertical-align: top; +} + +#tarteaucitronAlertSmall #tarteaucitronCookiesNumber:hover { + background: rgba(255, 255, 255, 0.3); +} + +#tarteaucitronAlertSmall #tarteaucitronCookiesListContainer { + display: none; + max-height: 70%; + max-width: 500px; + position: fixed; + right: 0; + width: 100%; +} + +#tarteaucitronAlertSmall #tarteaucitronCookiesListContainer #tarteaucitronCookiesList { + background: #fff; + border: 2px solid #333; + color: #333; + font-size: 11px; + height: auto; + overflow: auto; + text-align: left; +} + +#tarteaucitronAlertSmall #tarteaucitronCookiesListContainer #tarteaucitronCookiesList b { + color: #333; +} + +#tarteaucitronAlertSmall #tarteaucitronCookiesListContainer #tarteaucitronCookiesTitle { + background: #333; + margin-top: 21px; + padding: 13px 0 9px 13px; + text-align: left; +} + +#tarteaucitronAlertSmall #tarteaucitronCookiesListContainer #tarteaucitronCookiesTitle b { + color: #fff; + font-size: 16px; +} + +#tarteaucitronAlertSmall #tarteaucitronCookiesListContainer #tarteaucitronCookiesList .tarteaucitronCookiesListMain { + background: rgba(51, 51, 51, 0.1); + padding: 7px 5px 10px; + word-wrap: break-word; +} + +#tarteaucitronAlertSmall #tarteaucitronCookiesListContainer #tarteaucitronCookiesList .tarteaucitronCookiesListMain:hover { + background: rgba(51, 51, 51, 0.2); +} + +#tarteaucitronAlertSmall #tarteaucitronCookiesListContainer #tarteaucitronCookiesList .tarteaucitronCookiesListMain a { + color: #333; + text-decoration: none; +} + +#tarteaucitronAlertSmall #tarteaucitronCookiesListContainer #tarteaucitronCookiesList .tarteaucitronCookiesListMain .tarteaucitronCookiesListLeft { + display: inline-block; + width: 50%; +} + +#tarteaucitronAlertSmall #tarteaucitronCookiesListContainer #tarteaucitronCookiesList .tarteaucitronCookiesListMain .tarteaucitronCookiesListLeft a b { + color: darkred; +} + +#tarteaucitronAlertSmall #tarteaucitronCookiesListContainer #tarteaucitronCookiesList .tarteaucitronCookiesListMain .tarteaucitronCookiesListRight { + color: #333; + display: inline-block; + font-size: 11px; + margin-left: 10%; + vertical-align: top; + width: 30%; +} + +/*** + * Fallback activate link + */ +.tac_activate { + background: #333; + color: #fff; + display: table; + font-size: 12px; + height: 100%; + line-height: initial; + margin: auto; + text-align: center; + width: 100%; +} + +.tac_float { + display: table-cell; + text-align: center; + vertical-align: middle; +} + +.tac_activate .tac_float b { + color: #fff; +} + +.tac_activate .tac_float .tarteaucitronAllow { + background-color: #1B870B; + display: inline-block; +} + +/*** + * CSS for services + */ +ins.ferank-publicite, ins.adsbygoogle { + text-decoration: none; +} + +div.amazon_product { + height:240px; + width:120px; +} \ No newline at end of file diff --git a/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.cs.js b/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.cs.js new file mode 100755 index 0000000..057d71f --- /dev/null +++ b/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.cs.js @@ -0,0 +1,65 @@ +/*global tarteaucitron */ +tarteaucitron.lang = { + "adblock": "Ahoj! Tato stránka je transparetní a umožňuje ti si přímo vybrat, jaké služby třetích stran chceš povolit.", + "adblock_call": "Pro úpravu osobních preferencí si, prosím, vypni adblock.", + "reload": "Načíst stránku znovu", + + "alertBigScroll": "Pokračováním ve scrollování,", + "alertBigClick": "Pokud pokračujete v brouzdání našich stránek,", + "alertBig": "povolujete všechny služby třetích stran.", + + "alertBigPrivacy": "Tato stránka využívá cookies a dává ti na výběr, co chceš aktivovat", + "alertSmall": "Spravovat služby", + "personalize": "Přizpůsobit", + "acceptAll": "OK, přijmout vše", + "close": "Zavřít", + + "all": "Nastavení všech služeb", + + "info": "Chrání tvé soukromí", + "disclaimer": "Povolením těchto služeb třetích stran, přijímáš jejich cookies, jež jsou nezbytné pro řádné fungování jejich technologií.", + "allow": "Povolit", + "deny": "Zamítnout", + "noCookie": "Tato služba nepoužívá cookies.", + "useCookie": "Tato služba může nainstalovat", + "useCookieCurrent": "Tato služba nainstalovala", + "useNoCookie": "Tato služba nenainstalovala žádné cookies.", + "more": "Dozvědět se více", + "source": "Zobrazit oficiální stránku", + "credit": "Správce cookies od tarteaucitron.js", + + "fallback": "je vypnutý.", + + "ads": { + "title": "Reklamní síť", + "details": "Prodejem reklamních ploch na této stránce mohou reklamní sítě vydělávat peníze." + }, + "analytic": { + "title": "Statistika návštěvnosti", + "details": "Služby pro analýzu návštěvníků slouží k vytvoření užitečných statistik návštěvnosti. Ty zase slouží ke zlepšení stránky." + }, + "social": { + "title": "Sociální sítě", + "details": "Sociální sítě mohou usnadnit práci se stránkou a pomáhají jí prosadit se pomocí sdílení." + }, + "video": { + "title": "Videa", + "details": "Video-hostingové služby pomáhají přidat na stránku bohaté mediální prvky." + }, + "comment": { + "title": "Komentáře", + "details": "Správce komentářů zajišťují vyplňování komentářů a bojují proti šíření spamu." + }, + "support": { + "title": "Podpora", + "details": "Služby podpory ti pomáhají spojit se s týmem stojícím za stránkou a umožňují ti vyjádřit se k jejím nedostatkům." + }, + "api": { + "title": "API", + "details": "API slouží k načtění skriptů: geolokace, vyhledávačů, překladů, ..." + }, + "other": { + "title": "Jiný", + "details": "Služby pro zobrazení webového obsahu." + } +}; diff --git a/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.de.js b/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.de.js new file mode 100755 index 0000000..23cdc5f --- /dev/null +++ b/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.de.js @@ -0,0 +1,65 @@ +/*global tarteaucitron */ +tarteaucitron.lang = { + "adblock": "Hallo! Diese Seite ist transparent und lässt Ihnen die Wahl der externen Services, die aktiviert werden dürfen.", + "adblock_call": "Bitte deaktivieren Sie Ihren 'Werbeblocker' um Konfigurieren zu können.", + "reload": "Seite neu laden", + + "alertBigScroll": "Durch die fortgesetzte blättern,", + "alertBigClick": "Wenn Sie diese Webseite benutzen,", + "alertBig": "stimmen Sie der Benutzung von externen Diensten zu", + + "alertBigPrivacy": "Diese Webseite verwendet 'Cookies' und ermöglicht dadurch Kontrolle, welche Dienste benutzt werden dürfen", + "alertSmall": "Service-Kontrolle", + "personalize": "Personalisieren", + "acceptAll": "OK, akzeptiere alles", + "close": "Beenden", + + "all": "Präferenz für alle Dienste", + + "info": "Schutz der Privatsphäre", + "disclaimer": "Wenn Sie diese Dienste nutzen, erlauben Sie deren 'Cookies' und Tracking-Funktionen, die zu ihrer ordnungsgemäßen Funktion notwendig sind.", + "allow": "Erlauben", + "deny": "Ablehnen", + "noCookie": "Dieser Dienst nutzt keine 'Cookies'.", + "useCookie": "Dieser Dienst kann installieren", + "useCookieCurrent": "Dieser Dienst hat installiert", + "useNoCookie": "Dieser Dienst hat keine 'Cookies' installiert.", + "more": "Weiter lesen", + "source": "Zur offiziellen Webseite", + "credit": "Cookies manager von tarteaucitron.js", + + "fallback": "ist deaktiviert.", + + "ads": { + "title": "Anzeigen Netzwerke", + "details": "Anzeigen Netzwerke können mit dem Verkauf von Werbeplatzierungen auf der Seite Einnahmen erhalten." + }, + "analytic": { + "title": "Besucher Zähldienste", + "details": "Die verwendeten Besucher Zähldienste generieren Statistiken die dabei helfen, die Seite zu verbessern." + }, + "social": { + "title": "Soziale Netzwerke", + "details": "Soziale Netzwerke können die Benutzbarkeit der Seite verbessern und ihren Bekanntheitsgrad erhöhen." + }, + "video": { + "title": "Videos", + "details": "Video Platformen erlauben Videoinhalte einzublenden und die Sichtbarkeit der Seite zu erhöhen." + }, + "comment": { + "title": "Kommentare", + "details": "Kommentar Manager erleichtern die Organisation von Kommentaren und helfen dabei Spam zu verhindern." + }, + "support": { + "title": "Support", + "details": "Support Dienste erlauben es die Urheber der Seite zu kontaktieren und sie zu verbessern." + }, + "api": { + "title": "APIs", + "details": "APIs werden benutzt um Skripte zu laden, wie: Geolokalisation, Suchmaschinen, Übersetzungen, ..." + }, + "other": { + "title": "Andere", + "details": "Dienste zum Anzeigen von Web-Inhalten." + } +}; diff --git a/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.en.js b/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.en.js new file mode 100755 index 0000000..90c8ebb --- /dev/null +++ b/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.en.js @@ -0,0 +1,65 @@ +/*global tarteaucitron */ +tarteaucitron.lang = { + "adblock": "Hello! This site is transparent and lets you chose the 3rd party services you want to allow.", + "adblock_call": "Please disable your adblocker to start customizing.", + "reload": "Refresh the page", + + "alertBigScroll": "By continuing to scroll,", + "alertBigClick": "If you continue to browse this website,", + "alertBig": "you are allowing all third-party services", + + "alertBigPrivacy": "This site uses cookies and gives you control over what you want to activate", + "alertSmall": "Manage services", + "personalize": "Personalize", + "acceptAll": "OK, accept all", + "close": "Close", + + "all": "Preference for all services", + + "info": "Protecting your privacy", + "disclaimer": "By allowing these third party services, you accept their cookies and the use of tracking technologies necessary for their proper functioning.", + "allow": "Allow", + "deny": "Deny", + "noCookie": "This service does not use cookie.", + "useCookie": "This service can install", + "useCookieCurrent": "This service has installed", + "useNoCookie": "This service has not installed any cookie.", + "more": "Read more", + "source": "View the official website", + "credit": "Cookies manager by tarteaucitron.js", + + "fallback": "is disabled.", + + "ads": { + "title": "Advertising network", + "details": "Ad networks can generate revenue by selling advertising space on the site." + }, + "analytic": { + "title": "Audience measurement", + "details": "The audience measurement services used to generate useful statistics attendance to improve the site." + }, + "social": { + "title": "Social networks", + "details": "Social networks can improve the usability of the site and help to promote it via the shares." + }, + "video": { + "title": "Videos", + "details": "Video sharing services help to add rich media on the site and increase its visibility." + }, + "comment": { + "title": "Comments", + "details": "Comments managers facilitate the filing of comments and fight against spam." + }, + "support": { + "title": "Support", + "details": "Support services allow you to get in touch with the site team and help to improve it." + }, + "api": { + "title": "APIs", + "details": "APIs are used to load scripts: geolocation, search engines, translations, ..." + }, + "other": { + "title": "Other", + "details": "Services to display web content." + } +}; diff --git a/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.es.js b/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.es.js new file mode 100755 index 0000000..65e5f9b --- /dev/null +++ b/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.es.js @@ -0,0 +1,65 @@ +/*global tarteaucitron */ +tarteaucitron.lang = { + "adblock": "Hola! Este sitio web es transparente y le da la opción de activar los servicios de terceros.", + "adblock_call": "Por favor deshabilite su AdBlocker para comenzar a personalizar.", + "reload": "Actualizar esta página", + + "alertBigScroll": "Al continuar para desplazarse,", + "alertBigClick": "Si continuas navegando por este sitio web,", + "alertBig": "estar permitiendo servicios terceros", + + "alertBigPrivacy": "Este sitio web usa cookies y te permite controlar lo que deseas activar", + "alertSmall": "Gestionar servicios", + "personalize": "Personalizar", + "acceptAll": "OK, aceptar todas", + "close": "Cerrar", + + "all": "Preference for all services", + + "info": "Protegiendo tu privacidad", + "disclaimer": "Aceptando estos servicios terceros, estas aceptando sus cookies y el uso de tecnologías de rastreo necesarias para su correcto funcionamiento.", + "allow": "Permitir", + "deny": "Denegar", + "noCookie": "Este servicio no usa cookie.", + "useCookie": "Este servicio puede instalar", + "useCookieCurrent": "Este servicio ha instalado", + "useNoCookie": "Este servicio no ha instalado ninguna cookie.", + "more": "Leer más", + "source": "Ver sitio web oficial", + "credit": "Gestor de cookies realizada por tarteaucitron.js", + + "fallback": "esta deshabilitado.", + + "ads": { + "title": "Red de publicidad", + "details": "Las redes publicitarias pueden generar ingresos mediante la venta de espacios publicitarios en el sitio." + }, + "analytic": { + "title": "Mediciión de audiencia", + "details": "Los servicios de medición de audiencia se usan para generar asistencia estadísticas útiles para mejorar el sitio." + }, + "social": { + "title": "Redes sociales", + "details": "Las redes sociales pueden aumentar la usabilidad del sitio web y ayudar a promoverlo a través de la contribución." + }, + "video": { + "title": "Videos", + "details": "Los servicios para compartir videos ayudan a añadir contenido enriquecido en el sitio web y aumentar su visibilidad." + }, + "comment": { + "title": "Comentarios", + "details": "El gestor de comentarios facilita la clasificación de comentarios y luchar contra spam." + }, + "support": { + "title": "Soporte", + "details": "Los servicios de soporte te permiten contactar con el sitio web y ayudar a mejorarlo." + }, + "api": { + "title": "APIs", + "details": "APIs se utilizan para cargar scripts: geolocalización, motor de búsqueda, traducciones, ..." + }, + "other": { + "title": "Otro", + "details": "Servicios para mostrar contenido web." + } +}; diff --git a/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.fr.js b/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.fr.js new file mode 100755 index 0000000..6512403 --- /dev/null +++ b/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.fr.js @@ -0,0 +1,65 @@ +/*global tarteaucitron */ +tarteaucitron.lang = { + "adblock": "Bonjour! Ce site joue la transparence et vous donne le choix des services tiers à activer.", + "adblock_call": "Merci de désactiver votre adblocker pour commencer la personnalisation.", + "reload": "Recharger la page", + + "alertBigScroll": "En continuant de défiler,", + "alertBigClick": "En poursuivant votre navigation,", + "alertBig": "vous acceptez l'utilisation de services tiers pouvant installer des cookies", + + "alertBigPrivacy": "Ce site utilise des cookies et vous donne le contrôle sur ce que vous souhaitez activer", + "alertSmall": "Gestion des services", + "acceptAll": "OK, tout accepter", + "personalize": "Personnaliser", + "close": "Fermer", + + "all": "Préférence pour tous les services", + + "info": "Protection de votre vie privée", + "disclaimer": "En autorisant ces services tiers, vous acceptez le dépôt et la lecture de cookies et l'utilisation de technologies de suivi nécessaires à leur bon fonctionnement.", + "allow": "Autoriser", + "deny": "Interdire", + "noCookie": "Ce service ne dépose aucun cookie.", + "useCookie": "Ce service peut déposer", + "useCookieCurrent": "Ce service a déposé", + "useNoCookie": "Ce service n'a déposé aucun cookie.", + "more": "En savoir plus", + "source": "Voir le site officiel", + "credit": "Gestion des cookies par tarteaucitron.js", + + "fallback": "est désactivé.", + + "ads": { + "title": "Régies publicitaires", + "details": "Les régies publicitaires permettent de générer des revenus en commercialisant les espaces publicitaires du site." + }, + "analytic": { + "title": "Mesure d'audience", + "details": "Les services de mesure d'audience permettent de générer des statistiques de fréquentation utiles à l'amélioration du site." + }, + "social": { + "title": "Réseaux sociaux", + "details": "Les réseaux sociaux permettent d'améliorer la convivialité du site et aident à sa promotion via les partages." + }, + "video": { + "title": "Vidéos", + "details": "Les services de partage de vidéo permettent d'enrichir le site de contenu multimédia et augmentent sa visibilité." + }, + "comment": { + "title": "Commentaires", + "details": "Les gestionnaires de commentaires facilitent le dépôt de vos commentaires et luttent contre le spam." + }, + "support": { + "title": "Support", + "details": "Les services de support vous permettent d'entrer en contact avec l'équipe du site et d'aider à son amélioration." + }, + "api": { + "title": "APIs", + "details": "Les APIs permettent de charger des scripts : géolocalisation, moteurs de recherche, traductions, ..." + }, + "other": { + "title": "Autre", + "details": "Services visant à afficher du contenu web." + } +}; diff --git a/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.it.js b/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.it.js new file mode 100755 index 0000000..8b2faf1 --- /dev/null +++ b/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.it.js @@ -0,0 +1,65 @@ +/*global tarteaucitron */ +tarteaucitron.lang = { + "adblock": "Benvenuto! Questo sito ti permette di attivare i servizi di terzi di tua scelta.", + "adblock_call": "Disabilita il tuo adblocker per iniziare la navigazione.", + "reload": "Aggiorna la pagina", + + "alertBigScroll": "Continuando a scorrere,", + "alertBigClick": "Continuando a navigare nel sito,", + "alertBig": "autorizzi l’utilizzo dei cookies inviati da domini di terze parti", + + "alertBigPrivacy": "Questo sito fa uso di cookies e ti consente di decidere se accettarli o rifiutarli", + "alertSmall": "Gestione dei servizi", + "acceptAll": "Ok, accetta tutto", + "personalize": "Personalizza", + "close": "Chiudi", + + "all": "Preferenze per tutti i servizi", + + "info": "Tutela della privacy", + "disclaimer": "Abilitando l'uso dei servizi di terze parti, accetti la ricezione dei cookies e l'uso delle tecnologie analitici necessarie al loro funzionamento.", + "allow": "Consenti", + "deny": "Blocca", + "noCookie": "Questo servizio non invia nessun cookie", + "useCookie": "Questo servizio puo' inviare", + "useCookieCurrent": "Questo servizio ha inviato", + "useNoCookie": "Questo servizio non ha inviato nessun cookie", + "more": "Saperne di più", + "source": "Vai al sito ufficiale", + "credit": "Gestione dei cookies da tarteaucitron.js", + + "fallback": "è disattivato", + + "ads": { + "title": "Regie pubblicitarie", + "details": "Le regie pubblicitarie producono redditi gestendo la commercializzazione degli spazi del sito dedicati alle campagne pubblicitarie" + }, + "analytic": { + "title": "Misura del pubblico", + "details": "I servizi di misura del pubblico permettono di raccogliere le statistiche utili al miglioramento del sito" + }, + "social": { + "title": "Reti sociali", + "details": "Le reti sociali permettono di migliorare l'aspetto conviviale del sito e di sviluppare la condivisione dei contenuti da parte degli utenti a fini promozionali." + }, + "video": { + "title": "Video", + "details": "I servizi di condivisione di video permettono di arricchire il sito di contenuti multimediali e di aumentare la sua visibilità" + }, + "comment": { + "title": "Commenti", + "details": "La gestione dei commenti utente aiuta a gestire la pubblicazione dei commenti e a lottare contro lo spamming" + }, + "support": { + "title": "Supporto", + "details": "I servizi di supporto ti consentono di contattare la team del sito e di contribuire al suo miglioramento" + }, + "api": { + "title": "API", + "details": "Le API permettono di implementare script diversi : geolocalizzazione, motori di ricerca, traduttori..." + }, + "other": { + "title": "Altro", + "details": "Servizi per visualizzare contenuti web." + } +}; diff --git a/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.pl.js b/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.pl.js new file mode 100755 index 0000000..6fe5b8e --- /dev/null +++ b/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.pl.js @@ -0,0 +1,66 @@ +/*global tarteaucitron */ +tarteaucitron.lang = { + "adblock": "Witaj! Ta witryna oferuje przejrzystosc i daje mozliwosc wyboru aktywacji uslug zewnetrznych.", + "adblock_call": "Prosze wylaczyc adblocker aby rozpoczac dostosowanie do potrzeb uzytkownika.", + "reload": "Odswiez strone", + + "alertBigScroll": "Poprzez kontynuowanie przewijania,", + "alertBigClick": "Pozostajac na tej stronie", + "alertBig": "zgadzasz sie na korzystanie ze wszystkich zewnetrzynych uslug", + + "alertBigPrivacy": "Ta witryna używa plików cookie i pozwala kontrolować ich aktywacje", + "alertSmall": "Zarządzanie usługami", + "personalize": "Personalizacja", + "acceptAll": "OK, akceptuję wszystko", + "close": "zamknij", + + "all": "Preferencja dla wszystkich usług", + + "info": "Ochrona prywatności", + "disclaimer": "Zgadzajac sie na korzystanie z uslug zewnetrznych , akceptuje ich pliki cookies oraz wykorzystanie technologii niezbędnych do ich funkcjonowania.", + "allow": "Zezwalaj", + "deny": "Odmów", + "noCookie": "Ta usługa nie korzysta z plików cookie.", + "useCookie": "Ta usługa może zainstalować pliki cookie", + "useCookieCurrent": "Ta usługa zainstalowala plikie cookie", + "useNoCookie": "Ta usługa nie zainstalowala żadnego pliku cookie.", + "more": "Więcej informacji", + "source": "Zobacz oficjalną stronę internetowa", + "credit": "Cookies menadżer z tarteaucitron.js", + + "fallback": "jest nieaktywna.", + + "ads": { + "title": "Sieć reklamowa", + "details": "Sieci reklamowe mogą generować przychody ze sprzedaży powierzchni reklamowej na stronie." + }, + "analytic": { + "title": "Pomiar ogladalnosci", + "details": "Usługi pomiaru oglądalności wykorzystywane sa do generowania przydatnych statystyk potrzebnych w doskonaleniu strony." + }, + "social": { + "title": "Portale społecznościowe", + "details": "Sieci społecznościowe mogą poprawić użyteczność serwisu i pomóc w promocji za pośrednictwem propagacji strony." + }, + "video": { + "title": "Filmy", + "details": "Usługa udostępniania wideo pomoże dodać multimedia do strony i zwiększyć jej ogladalność." + }, + "comment": { + "title": "Komentarze", + "details": "Zarządzanie komentarzami ułatwia komentowanie i zwalcza spam." + }, + "support": { + "title": "Pomoc", + "details": "Usługa pomocy technicznej pozwala, skontaktować się z administratorem witryny i pomaga ją udoskonalić." + }, + "api": { + "title": "APIs", + "details": "APIs służą do ładowania skryptów: geolokalizacji, wyszukiwarek, tłumaczenia, ..." + }, + "other": { + "title": "Inny", + "details": "Usługi do wyświetlania treści internetowych." + } + +}; diff --git a/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.pt.js b/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.pt.js new file mode 100755 index 0000000..690fd20 --- /dev/null +++ b/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.pt.js @@ -0,0 +1,61 @@ +/*global tarteaucitron */ +tarteaucitron.lang = { + "adblock": "Olá! Em uma açao de transparencia, este site lhe dá a opção de quais serviços terceiros deseje ativar.", + "adblock_call": "Por favor, desative seu bloqueador de publicidades para poder customizar.", + "reload": "Atualizar esta página", + + "alertBigScroll": "Ao continuar a rolar,", + "alertBigClick": "Se você continuar a navegaçao neste site,", + "alertBig": "você estará aceitando todos os serviços terceiros", + + "alertBigPrivacy": "Esse site utiliza cookies and lhe dá controle sobre o que você quer ativar", + "alertSmall": "Gerenciar serviços", + "personalize": "Personalizar", + "acceptAll": "OK, aceitar tudo", + "close": "Fechar", + "all": "Definições dos serviços", + "info": "Proteger sua privacidade", + "disclaimer": "Ao aceitar os serviços terceiros, você aceita o uso de cookies em conjunto de tecnologias de rastreamento que lhe são necessárias para funcionar", + "allow": "Autorizar", + "deny": "Recusar", + "noCookie": "Este serviço não usa cookies.", + "useCookie": "Este serviço pode instalar", + "useCookieCurrent": "Este serviço instalou", + "useNoCookie": "Este serviço não instalou nenhum cookie.", + "more": "Ler mais", + "source": "Ver o site oficial", + "credit": "Gerenciador de cookies por tarteaucitron.js", + "fallback": "está desativado.", + "ads": { + "title": "Rede de anúncios", + "details": "As redes de anúncios podem gerar receitas com a venda de espaço publicitário no site." + }, + "analytic": { + "title": "Medição de audiência", + "details": "Serviços de medição de audiência usados para gerar estatísticas no intuito de melhorar o site." + }, + "social": { + "title": "Rede sociais", + "details": "Rede sociais podem ameliorar o utilização do site e ajudar a promove-lo via compartilhamentos." + }, + "video": { + "title": "Vídeos", + "details": "Video sharing services help to add rich media on the site and increase its visibility." + }, + "comment": { + "title": "Comentários", + "details": "Gerenciadores de comentários facilitam o sistema de comentários e lutam contra o spam." + }, + "support": { + "title": "Suporte", + "details": "Serviços de suporte lhe ajudam a entrar em contato com a equipe de suporte." + }, + "api": { + "title": "APIs", + "details": "APIs são usadas para carregar scripts: geolocalização, motores de pesquisa, traduções, ..." + }, + "other": { + "title": "De outros", + "details": "Serviços para exibir conteúdo da web." + } +}; diff --git a/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.ru.js b/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.ru.js new file mode 100755 index 0000000..d99370c --- /dev/null +++ b/docs/pages/assets/js/tarteaucitron/lang/tarteaucitron.ru.js @@ -0,0 +1,65 @@ +/*global tarteaucitron */ +tarteaucitron.lang = { + "adblock": "Привет! Этот сайт совершенно открытый и позволяет вам выбрать сервисы третьих лиц, которым вы хотите дать доступ.", + "adblock_call": "Пожалуйста дезактивируйте АдБлокер чтобы начать настройку.", + "reload": "Перезагрузите страницу", + + "alertBigScroll": "Продолжая прокрутки", + "alertBigClick": "Если вы продолжаете использовать сайт", + "alertBig": "вы позволяете сервисы третьих лиц", + + "alertBigPrivacy": "Этот сайт использует кукис и позволяет вам контролировать сервисы которые вы хотите активировать", + "alertSmall": "Настройка сервисов", + "personalize": "Персонализировать", + "acceptAll": "Ок, все активировать", + "close": "Закрыть", + + "all": "Преференция всем сервисам", + + "info": "Защитить вашу конфиденциальность", + "disclaimer": "Активирование сервисов третьих лиц позволяет использование их кукис и технолоний отслеживания необходимых для их функционирования", + "allow": "Позролить", + "deny": "Не позволить", + "noCookie": "Этот сервис не использует кукис.", + "useCookie": "Этот сервис может быть инсталирован", + "useCookieCurrent": "Этот сервис инсталирован", + "useNoCookie": "Этот сервис не использует кукис.", + "more": "Подробнее", + "source": "Посетите официальный сайт", + "credit": "Кукис манаджер tarteaucitron.js", + + "fallback": "Деактивирован.", + + "ads": { + "title": "Рекламная сеть", + "details": "Мы позволяем вам аренду нашей рекламной сети." + }, + "analytic": { + "title": "Измерение аудиенции", + "details": "Измерение аудиенции сайта для статистики помогают улучшить предлагаемый сервис." + }, + "social": { + "title": "Социальная сеть", + "details": "Социальная сеть сайтов помогает улучшить предлагаемый сервис через обмен информации." + }, + "video": { + "title": "Видео", + "details": "Обмен видео информации позволяет улучшить сервис и увеличит траффик сайта." + }, + "comment": { + "title": "Комментарии", + "details": "Манаджер комментариев позволяет обмен информации и борьбу со спамом." + }, + "support": { + "title": "Помощь", + "details": "Помощь позволяет вам контактировать напрямую сайт манаджер и улучшить предлагаемый сервис." + }, + "api": { + "title": "АПИ", + "details": "АПИ используются для загрузки скриптов; геолокация, поисковый мотор и переводы..." + }, + "other": { + "title": "Другие", + "details": "Службы для отображения веб-контента." + } +}; diff --git a/docs/pages/assets/js/tarteaucitron/tarteaucitron.js b/docs/pages/assets/js/tarteaucitron/tarteaucitron.js new file mode 100755 index 0000000..88cd673 --- /dev/null +++ b/docs/pages/assets/js/tarteaucitron/tarteaucitron.js @@ -0,0 +1,1265 @@ +/*jslint browser: true, evil: true */ + +// define correct path for files inclusion +var scripts = document.getElementsByTagName('script'), + path = scripts[scripts.length - 1].src.split('?')[0], + cdn = path.split('/').slice(0, -1).join('/') + '/', + alreadyLaunch = (alreadyLaunch === undefined) ? 0 : alreadyLaunch, + tarteaucitronForceLanguage = (tarteaucitronForceLanguage === undefined) ? '' : tarteaucitronForceLanguage, + tarteaucitronProLoadServices, + tarteaucitronNoAdBlocker = false; + +var tarteaucitron = { + "version": 323, + "cdn": cdn, + "user": {}, + "lang": {}, + "services": {}, + "added": [], + "idprocessed": [], + "state": [], + "launch": [], + "parameters": {}, + "isAjax": false, + "reloadThePage": false, + "init": function (params) { + "use strict"; + var origOpen; + + tarteaucitron.parameters = params; + if (alreadyLaunch === 0) { + alreadyLaunch = 1; + if (window.addEventListener) { + window.addEventListener("load", function () { + tarteaucitron.load(); + tarteaucitron.fallback(['tarteaucitronOpenPanel'], function (elem) { + elem.addEventListener("click", function (event) { + tarteaucitron.userInterface.openPanel(); + event.preventDefault(); + }, false); + }, true); + }, false); + window.addEventListener("scroll", function () { + var scrollPos = window.pageYOffset || document.documentElement.scrollTop, + heightPosition; + if (document.getElementById('tarteaucitronAlertBig') !== null && !tarteaucitron.highPrivacy) { + if (document.getElementById('tarteaucitronAlertBig').style.display === 'block') { + heightPosition = document.getElementById('tarteaucitronAlertBig').offsetHeight + 'px'; + + if (scrollPos > (screen.height * 2)) { + tarteaucitron.userInterface.respondAll(true); + } else if (scrollPos > (screen.height / 2)) { + document.getElementById('tarteaucitronDisclaimerAlert').innerHTML = '' + tarteaucitron.lang.alertBigScroll + ' ' + tarteaucitron.lang.alertBig; + } + + if (tarteaucitron.orientation === 'top') { + document.getElementById('tarteaucitronPercentage').style.top = heightPosition; + } else { + document.getElementById('tarteaucitronPercentage').style.bottom = heightPosition; + } + document.getElementById('tarteaucitronPercentage').style.width = ((100 / (screen.height * 2)) * scrollPos) + '%'; + } + } + }, false); + window.addEventListener("keydown", function (evt) { + if (evt.keyCode === 27) { + tarteaucitron.userInterface.closePanel(); + } + }, false); + window.addEventListener("hashchange", function () { + if (document.location.hash === tarteaucitron.hashtag && tarteaucitron.hashtag !== '') { + tarteaucitron.userInterface.openPanel(); + } + }, false); + window.addEventListener("resize", function () { + if (document.getElementById('tarteaucitron') !== null) { + if (document.getElementById('tarteaucitron').style.display === 'block') { + tarteaucitron.userInterface.jsSizing('main'); + } + } + + if (document.getElementById('tarteaucitronCookiesListContainer') !== null) { + if (document.getElementById('tarteaucitronCookiesListContainer').style.display === 'block') { + tarteaucitron.userInterface.jsSizing('cookie'); + } + } + }, false); + } else { + window.attachEvent("onload", function () { + tarteaucitron.load(); + tarteaucitron.fallback(['tarteaucitronOpenPanel'], function (elem) { + elem.attachEvent("onclick", function (event) { + tarteaucitron.userInterface.openPanel(); + event.preventDefault(); + }); + }, true); + }); + window.attachEvent("onscroll", function () { + var scrollPos = window.pageYOffset || document.documentElement.scrollTop, + heightPosition; + if (document.getElementById('tarteaucitronAlertBig') !== null && !tarteaucitron.highPrivacy) { + if (document.getElementById('tarteaucitronAlertBig').style.display === 'block') { + heightPosition = document.getElementById('tarteaucitronAlertBig').offsetHeight + 'px'; + + if (scrollPos > (screen.height * 2)) { + tarteaucitron.userInterface.respondAll(true); + } else if (scrollPos > (screen.height / 2)) { + document.getElementById('tarteaucitronDisclaimerAlert').innerHTML = '' + tarteaucitron.lang.alertBigScroll + ' ' + tarteaucitron.lang.alertBig; + } + if (tarteaucitron.orientation === 'top') { + document.getElementById('tarteaucitronPercentage').style.top = heightPosition; + } else { + document.getElementById('tarteaucitronPercentage').style.bottom = heightPosition; + } + document.getElementById('tarteaucitronPercentage').style.width = ((100 / (screen.height * 2)) * scrollPos) + '%'; + } + } + }); + window.attachEvent("onkeydown", function (evt) { + if (evt.keyCode === 27) { + tarteaucitron.userInterface.closePanel(); + } + }); + window.attachEvent("onhashchange", function () { + if (document.location.hash === tarteaucitron.hashtag && tarteaucitron.hashtag !== '') { + tarteaucitron.userInterface.openPanel(); + } + }); + window.attachEvent("onresize", function () { + if (document.getElementById('tarteaucitron') !== null) { + if (document.getElementById('tarteaucitron').style.display === 'block') { + tarteaucitron.userInterface.jsSizing('main'); + } + } + + if (document.getElementById('tarteaucitronCookiesListContainer') !== null) { + if (document.getElementById('tarteaucitronCookiesListContainer').style.display === 'block') { + tarteaucitron.userInterface.jsSizing('cookie'); + } + } + }); + } + + if (typeof XMLHttpRequest !== 'undefined') { + origOpen = XMLHttpRequest.prototype.open; + XMLHttpRequest.prototype.open = function () { + + if (window.addEventListener) { + this.addEventListener("load", function () { + if (typeof tarteaucitronProLoadServices === 'function') { + tarteaucitronProLoadServices(); + } + }, false); + } else if (typeof this.attachEvent !== 'undefined') { + this.attachEvent("onload", function () { + if (typeof tarteaucitronProLoadServices === 'function') { + tarteaucitronProLoadServices(); + } + }); + } else { + if (typeof tarteaucitronProLoadServices === 'function') { + setTimeout(tarteaucitronProLoadServices, 1000); + } + } + + try { + origOpen.apply(this, arguments); + } catch (err) {} + }; + } + } + }, + "load": function () { + "use strict"; + var cdn = tarteaucitron.cdn, + language = tarteaucitron.getLanguage(), + pathToLang = cdn + 'lang/tarteaucitron.' + language + '.js?v=' + tarteaucitron.version, + pathToServices = cdn + 'tarteaucitron.services.js?v=' + tarteaucitron.version, + linkElement = document.createElement('link'), + defaults = { + "adblocker": false, + "hashtag": '#tarteaucitron', + "highPrivacy": false, + "orientation": "top", + "removeCredit": false, + "showAlertSmall": true, + "cookieslist": true + }, + params = tarteaucitron.parameters; + + // Step 0: get params + if (params !== undefined) { + tarteaucitron.extend(defaults, params); + } + + // global + tarteaucitron.orientation = defaults.orientation; + tarteaucitron.hashtag = defaults.hashtag; + tarteaucitron.highPrivacy = defaults.highPrivacy; + + // Step 1: load css + linkElement.rel = 'stylesheet'; + linkElement.type = 'text/css'; + linkElement.href = cdn + 'css/tarteaucitron.css?v=' + tarteaucitron.version; + document.getElementsByTagName('head')[0].appendChild(linkElement); + + // Step 2: load language and services + tarteaucitron.addScript(pathToLang, '', function () { + tarteaucitron.addScript(pathToServices, '', function () { + + var body = document.body, + div = document.createElement('div'), + html = '', + index, + orientation = 'Top', + cat = ['ads', 'analytic', 'api', 'comment', 'social', 'support', 'video', 'other'], + i; + + cat = cat.sort(function (a, b) { + if (tarteaucitron.lang[a].title > tarteaucitron.lang[b].title) { return 1; } + if (tarteaucitron.lang[a].title < tarteaucitron.lang[b].title) { return -1; } + return 0; + }); + + // Step 3: prepare the html + html += '
'; + html += '
'; + html += '
'; + html += '
'; + html += ' ' + tarteaucitron.lang.close; + html += '
'; + html += '
'; + html += '
'; + html += '
'; + html += ' ' + tarteaucitron.lang.all + ''; + html += '
'; + html += '
'; + html += '
'; + html += ' ✓ ' + tarteaucitron.lang.allow; + html += '
'; + html += '
'; + html += ' ✗ ' + tarteaucitron.lang.deny; + html += '
'; + html += '
'; + html += '
'; + html += '
'; + html += ' ' + tarteaucitron.lang.disclaimer; + if (defaults.removeCredit === false) { + html += '

'; + html += ' ' + tarteaucitron.lang.credit + ''; + } + html += '
'; + html += '
'; + html += '
'; + for (i = 0; i < cat.length; i += 1) { + html += '
'; + html += '
'; + html += ' ' + tarteaucitron.lang[cat[i]].title; + html += '
'; + html += '
'; + html += ' ' + tarteaucitron.lang[cat[i]].details; + html += '
'; + html += '
'; + html += '
'; + } + html += '
'; + html += '
'; + html += '
'; + html += '
'; + + if (defaults.orientation === 'bottom') { + orientation = 'Bottom'; + } + + if (defaults.highPrivacy) { + html += '
'; + html += ' '; + html += ' ' + tarteaucitron.lang.alertBigPrivacy; + html += ' '; + html += ' '; + html += ' ' + tarteaucitron.lang.personalize; + html += ' '; + html += '
'; + } else { + html += '
'; + html += ' '; + html += ' ' + tarteaucitron.lang.alertBigClick + ' ' + tarteaucitron.lang.alertBig; + html += ' '; + html += ' '; + html += ' ✓ ' + tarteaucitron.lang.acceptAll; + html += ' '; + html += ' '; + html += ' ' + tarteaucitron.lang.personalize; + html += ' '; + html += '
'; + html += '
'; + } + + if (defaults.showAlertSmall === true) { + html += '
'; + html += '
'; + html += ' ' + tarteaucitron.lang.alertSmall; + html += '
'; + html += ' '; + html += ' '; + html += ' '; + html += '
'; + if (defaults.cookieslist === true) { + html += '
0
'; + html += '
'; + html += '
'; + html += ' ' + tarteaucitron.lang.close; + html += '
'; + html += '
'; + html += ' 0 cookie'; + html += '
'; + html += '
'; + html += '
'; + } else { + html += '
'; + } + html += ''; + } + + tarteaucitron.addScript(tarteaucitron.cdn + 'advertising.js?v=' + tarteaucitron.version, '', function () { + if (tarteaucitronNoAdBlocker === true || defaults.adblocker === false) { + div.id = 'tarteaucitronRoot'; + body.appendChild(div, body); + div.innerHTML = html; + + if (tarteaucitron.job !== undefined) { + tarteaucitron.job = tarteaucitron.cleanArray(tarteaucitron.job); + for (index = 0; index < tarteaucitron.job.length; index += 1) { + tarteaucitron.addService(tarteaucitron.job[index]); + } + } + + tarteaucitron.isAjax = true; + tarteaucitron.job.push = function (id) { + + // ie <9 hack + if (typeof tarteaucitron.job.indexOf === 'undefined') { + tarteaucitron.job.indexOf = function (obj, start) { + var i, + j = this.length; + for (i = (start || 0); i < j; i += 1) { + if (this[i] === obj) { return i; } + } + return -1; + }; + } + + if (tarteaucitron.job.indexOf(id) === -1) { + Array.prototype.push.call(this, id); + } + tarteaucitron.launch[id] = false; + tarteaucitron.addService(id); + }; + + if (document.location.hash === tarteaucitron.hashtag && tarteaucitron.hashtag !== '') { + tarteaucitron.userInterface.openPanel(); + } + + tarteaucitron.cookie.number(); + setInterval(tarteaucitron.cookie.number, 60000); + } + }, defaults.adblocker); + + if (defaults.adblocker === true) { + setTimeout(function () { + if (tarteaucitronNoAdBlocker === false) { + html = '
'; + html += ' '; + html += ' ' + tarteaucitron.lang.adblock + '
'; + html += ' ' + tarteaucitron.lang.adblock_call + ''; + html += '
'; + html += ' '; + html += ' ' + tarteaucitron.lang.reload; + html += ' '; + html += '
'; + html += '
'; + div.id = 'tarteaucitronRoot'; + body.appendChild(div, body); + div.innerHTML = html; + tarteaucitron.pro('!adblocker=true'); + } else { + tarteaucitron.pro('!adblocker=false'); + } + }, 1500); + } + }); + }); + }, + "addService": function (serviceId) { + "use strict"; + var html = '', + s = tarteaucitron.services, + service = s[serviceId], + cookie = tarteaucitron.cookie.read(), + hostname = document.location.hostname, + hostRef = document.referrer.split('/')[2], + isNavigating = (hostRef === hostname) ? true : false, + isAutostart = (!service.needConsent) ? true : false, + isWaiting = (cookie.indexOf(service.key + '=wait') >= 0) ? true : false, + isDenied = (cookie.indexOf(service.key + '=false') >= 0) ? true : false, + isAllowed = (cookie.indexOf(service.key + '=true') >= 0) ? true : false, + isResponded = (cookie.indexOf(service.key + '=false') >= 0 || cookie.indexOf(service.key + '=true') >= 0) ? true : false; + + if (tarteaucitron.added[service.key] !== true) { + tarteaucitron.added[service.key] = true; + + html += '
'; + html += '
'; + html += ' ' + service.name + '
'; + html += '
'; + html += ' '; + html += ' ' + tarteaucitron.lang.more; + html += ' '; + html += ' - '; + html += ' '; + html += ' ' + tarteaucitron.lang.source; + html += ' '; + html += '
'; + html += '
'; + html += '
'; + html += ' ✓ ' + tarteaucitron.lang.allow; + html += '
'; + html += '
'; + html += ' ✗ ' + tarteaucitron.lang.deny; + html += '
'; + html += '
'; + html += '
'; + + tarteaucitron.userInterface.css('tarteaucitronServicesTitle_' + service.type, 'display', 'block'); + + if (document.getElementById('tarteaucitronServices_' + service.type) !== null) { + document.getElementById('tarteaucitronServices_' + service.type).innerHTML += html; + } + + tarteaucitron.userInterface.order(service.type); + } + + // allow by default for non EU + if (isResponded === false && tarteaucitron.user.bypass === true) { + isAllowed = true; + tarteaucitron.cookie.create(service.key, true); + } + + if ((!isResponded && (isAutostart || (isNavigating && isWaiting)) && !tarteaucitron.highPrivacy) || isAllowed) { + if (!isAllowed) { + tarteaucitron.cookie.create(service.key, true); + } + if (tarteaucitron.launch[service.key] !== true) { + tarteaucitron.launch[service.key] = true; + service.js(); + } + tarteaucitron.state[service.key] = true; + tarteaucitron.userInterface.color(service.key, true); + } else if (isDenied) { + if (typeof service.fallback === 'function') { + service.fallback(); + } + tarteaucitron.state[service.key] = false; + tarteaucitron.userInterface.color(service.key, false); + } else if (!isResponded) { + tarteaucitron.cookie.create(service.key, 'wait'); + if (typeof service.fallback === 'function') { + service.fallback(); + } + tarteaucitron.userInterface.color(service.key, 'wait'); + tarteaucitron.userInterface.openAlert(); + } + + tarteaucitron.cookie.checkCount(service.key); + }, + "cleanArray": function cleanArray(arr) { + "use strict"; + var i, + len = arr.length, + out = [], + obj = {}, + s = tarteaucitron.services; + + for (i = 0; i < len; i += 1) { + if (!obj[arr[i]]) { + obj[arr[i]] = {}; + if (tarteaucitron.services[arr[i]] !== undefined) { + out.push(arr[i]); + } + } + } + + out = out.sort(function (a, b) { + if (s[a].type + s[a].key > s[b].type + s[b].key) { return 1; } + if (s[a].type + s[a].key < s[b].type + s[b].key) { return -1; } + return 0; + }); + + return out; + }, + "userInterface": { + "css": function (id, property, value) { + "use strict"; + if (document.getElementById(id) !== null) { + document.getElementById(id).style[property] = value; + } + }, + "respondAll": function (status) { + "use strict"; + var s = tarteaucitron.services, + service, + key, + index = 0; + + for (index = 0; index < tarteaucitron.job.length; index += 1) { + service = s[tarteaucitron.job[index]]; + key = service.key; + if (tarteaucitron.state[key] !== status) { + if (status === false && tarteaucitron.launch[key] === true) { + tarteaucitron.reloadThePage = true; + } + if (tarteaucitron.launch[key] !== true && status === true) { + tarteaucitron.launch[key] = true; + tarteaucitron.services[key].js(); + } + tarteaucitron.state[key] = status; + tarteaucitron.cookie.create(key, status); + tarteaucitron.userInterface.color(key, status); + } + } + }, + "respond": function (el, status) { + "use strict"; + var key = el.id.replace(new RegExp("(Eng[0-9]+|Allow|Deni)ed", "g"), ''); + + // return if same state + if (tarteaucitron.state[key] === status) { + return; + } + + if (status === false && tarteaucitron.launch[key] === true) { + tarteaucitron.reloadThePage = true; + } + + // if not already launched... launch the service + if (status === true) { + if (tarteaucitron.launch[key] !== true) { + tarteaucitron.launch[key] = true; + tarteaucitron.services[key].js(); + } + } + tarteaucitron.state[key] = status; + tarteaucitron.cookie.create(key, status); + tarteaucitron.userInterface.color(key, status); + }, + "color": function (key, status) { + "use strict"; + var gray = '#808080', + greenDark = '#1B870B', + greenLight = '#E6FFE2', + redDark = '#9C1A1A', + redLight = '#FFE2E2', + yellowDark = '#FBDA26', + c = 'tarteaucitron', + nbDenied = 0, + nbPending = 0, + nbAllowed = 0, + sum = tarteaucitron.job.length, + index; + + if (status === true) { + tarteaucitron.userInterface.css(key + 'Line', 'borderLeft', '5px solid ' + greenDark); + tarteaucitron.userInterface.css(key + 'Allowed', 'backgroundColor', greenDark); + tarteaucitron.userInterface.css(key + 'Denied', 'backgroundColor', gray); + } else if (status === false) { + tarteaucitron.userInterface.css(key + 'Line', 'borderLeft', '5px solid ' + redDark); + tarteaucitron.userInterface.css(key + 'Allowed', 'backgroundColor', gray); + tarteaucitron.userInterface.css(key + 'Denied', 'backgroundColor', redDark); + } + + // check if all services are allowed + for (index = 0; index < sum; index += 1) { + if (tarteaucitron.state[tarteaucitron.job[index]] === false) { + nbDenied += 1; + } else if (tarteaucitron.state[tarteaucitron.job[index]] === undefined) { + nbPending += 1; + } else if (tarteaucitron.state[tarteaucitron.job[index]] === true) { + nbAllowed += 1; + } + } + + tarteaucitron.userInterface.css(c + 'DotGreen', 'width', ((100 / sum) * nbAllowed) + '%'); + tarteaucitron.userInterface.css(c + 'DotYellow', 'width', ((100 / sum) * nbPending) + '%'); + tarteaucitron.userInterface.css(c + 'DotRed', 'width', ((100 / sum) * nbDenied) + '%'); + + if (nbDenied === 0 && nbPending === 0) { + tarteaucitron.userInterface.css(c + 'AllAllowed', 'backgroundColor', greenDark); + tarteaucitron.userInterface.css(c + 'AllDenied', 'backgroundColor', gray); + } else if (nbAllowed === 0 && nbPending === 0) { + tarteaucitron.userInterface.css(c + 'AllAllowed', 'backgroundColor', gray); + tarteaucitron.userInterface.css(c + 'AllDenied', 'backgroundColor', redDark); + } else { + tarteaucitron.userInterface.css(c + 'AllAllowed', 'backgroundColor', gray); + tarteaucitron.userInterface.css(c + 'AllDenied', 'backgroundColor', gray); + } + + // close the alert if all service have been reviewed + if (nbPending === 0) { + tarteaucitron.userInterface.closeAlert(); + } + + if (tarteaucitron.services[key].cookies.length > 0 && status === false) { + tarteaucitron.cookie.purge(tarteaucitron.services[key].cookies); + } + + if (status === true) { + if (document.getElementById('tacCL' + key) !== null) { + document.getElementById('tacCL' + key).innerHTML = '...'; + } + setTimeout(function () { + tarteaucitron.cookie.checkCount(key); + }, 2500); + } else { + tarteaucitron.cookie.checkCount(key); + } + }, + "openPanel": function () { + "use strict"; + tarteaucitron.userInterface.css('tarteaucitron', 'display', 'block'); + tarteaucitron.userInterface.css('tarteaucitronBack', 'display', 'block'); + tarteaucitron.userInterface.css('tarteaucitronCookiesListContainer', 'display', 'none'); + tarteaucitron.userInterface.jsSizing('main'); + }, + "closePanel": function () { + "use strict"; + + if (document.location.hash === tarteaucitron.hashtag) { + document.location.hash = ''; + } + tarteaucitron.userInterface.css('tarteaucitron', 'display', 'none'); + tarteaucitron.userInterface.css('tarteaucitronCookiesListContainer', 'display', 'none'); + + tarteaucitron.fallback(['tarteaucitronInfoBox'], function (elem) { + elem.style.display = 'none'; + }, true); + + if (tarteaucitron.reloadThePage === true) { + window.location.reload(); + } else { + tarteaucitron.userInterface.css('tarteaucitronBack', 'display', 'none'); + } + }, + "openAlert": function () { + "use strict"; + var c = 'tarteaucitron'; + tarteaucitron.userInterface.css(c + 'Percentage', 'display', 'block'); + tarteaucitron.userInterface.css(c + 'AlertSmall', 'display', 'none'); + tarteaucitron.userInterface.css(c + 'AlertBig', 'display', 'block'); + }, + "closeAlert": function () { + "use strict"; + var c = 'tarteaucitron'; + tarteaucitron.userInterface.css(c + 'Percentage', 'display', 'none'); + tarteaucitron.userInterface.css(c + 'AlertSmall', 'display', 'block'); + tarteaucitron.userInterface.css(c + 'AlertBig', 'display', 'none'); + tarteaucitron.userInterface.jsSizing('box'); + }, + "toggleCookiesList": function () { + "use strict"; + var div = document.getElementById('tarteaucitronCookiesListContainer'); + + if (div === null) { + return; + } + + if (div.style.display !== 'block') { + tarteaucitron.cookie.number(); + div.style.display = 'block'; + tarteaucitron.userInterface.jsSizing('cookie'); + tarteaucitron.userInterface.css('tarteaucitron', 'display', 'none'); + tarteaucitron.userInterface.css('tarteaucitronBack', 'display', 'block'); + tarteaucitron.fallback(['tarteaucitronInfoBox'], function (elem) { + elem.style.display = 'none'; + }, true); + } else { + div.style.display = 'none'; + tarteaucitron.userInterface.css('tarteaucitron', 'display', 'none'); + tarteaucitron.userInterface.css('tarteaucitronBack', 'display', 'none'); + } + }, + "toggle": function (id, closeClass) { + "use strict"; + var div = document.getElementById(id); + + if (div === null) { + return; + } + + if (closeClass !== undefined) { + tarteaucitron.fallback([closeClass], function (elem) { + if (elem.id !== id) { + elem.style.display = 'none'; + } + }, true); + } + + if (div.style.display !== 'block') { + div.style.display = 'block'; + } else { + div.style.display = 'none'; + } + }, + "order": function (id) { + "use strict"; + var main = document.getElementById('tarteaucitronServices_' + id), + allDivs, + store = [], + i; + + if (main === null) { + return; + } + + allDivs = main.childNodes; + + if (typeof Array.prototype.map === 'function') { + Array.prototype.map.call(main.children, Object).sort(function (a, b) { + if (tarteaucitron.services[a.id.replace(/Line/g, '')].name > tarteaucitron.services[b.id.replace(/Line/g, '')].name) { return 1; } + if (tarteaucitron.services[a.id.replace(/Line/g, '')].name < tarteaucitron.services[b.id.replace(/Line/g, '')].name) { return -1; } + return 0; + }).forEach(function (element) { + main.appendChild(element); + }); + } + }, + "jsSizing": function (type) { + "use strict"; + var scrollbarMarginRight = 10, + scrollbarWidthParent, + scrollbarWidthChild, + servicesHeight, + e = window, + a = 'inner', + windowInnerHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight, + mainTop, + mainHeight, + closeButtonHeight, + headerHeight, + cookiesListHeight, + cookiesCloseHeight, + cookiesTitleHeight, + paddingBox, + alertSmallHeight, + cookiesNumberHeight; + + if (type === 'box') { + if (document.getElementById('tarteaucitronAlertSmall') !== null && document.getElementById('tarteaucitronCookiesNumber') !== null) { + + // reset + tarteaucitron.userInterface.css('tarteaucitronCookiesNumber', 'padding', '0px 10px'); + + // calculate + alertSmallHeight = document.getElementById('tarteaucitronAlertSmall').offsetHeight; + cookiesNumberHeight = document.getElementById('tarteaucitronCookiesNumber').offsetHeight; + paddingBox = (alertSmallHeight - cookiesNumberHeight) / 2; + + // apply + tarteaucitron.userInterface.css('tarteaucitronCookiesNumber', 'padding', paddingBox + 'px 10px'); + } + } else if (type === 'main') { + + // get the real window width for media query + if (window.innerWidth === undefined) { + a = 'client'; + e = document.documentElement || document.body; + } + + // height of the services list container + if (document.getElementById('tarteaucitron') !== null && document.getElementById('tarteaucitronClosePanel') !== null && document.getElementById('tarteaucitronMainLineOffset') !== null) { + + // reset + tarteaucitron.userInterface.css('tarteaucitronScrollbarParent', 'height', 'auto'); + + // calculate + mainHeight = document.getElementById('tarteaucitron').offsetHeight; + closeButtonHeight = document.getElementById('tarteaucitronClosePanel').offsetHeight; + headerHeight = document.getElementById('tarteaucitronMainLineOffset').offsetHeight; + + // apply + servicesHeight = (mainHeight - closeButtonHeight - headerHeight + 1); + tarteaucitron.userInterface.css('tarteaucitronScrollbarParent', 'height', servicesHeight + 'px'); + } + + // align the main allow/deny button depending on scrollbar width + if (document.getElementById('tarteaucitronScrollbarParent') !== null && document.getElementById('tarteaucitronScrollbarChild') !== null) { + + // media query + if (e[a + 'Width'] <= 479) { + tarteaucitron.userInterface.css('tarteaucitronScrollbarAdjust', 'marginLeft', '11px'); + } else if (e[a + 'Width'] <= 767) { + scrollbarMarginRight = 12; + } + + scrollbarWidthParent = document.getElementById('tarteaucitronScrollbarParent').offsetWidth; + scrollbarWidthChild = document.getElementById('tarteaucitronScrollbarChild').offsetWidth; + tarteaucitron.userInterface.css('tarteaucitronScrollbarAdjust', 'marginRight', ((scrollbarWidthParent - scrollbarWidthChild) + scrollbarMarginRight) + 'px'); + } + + // center the main panel + if (document.getElementById('tarteaucitron') !== null) { + + // media query + if (e[a + 'Width'] <= 767) { + mainTop = 0; + } else { + mainTop = ((windowInnerHeight - document.getElementById('tarteaucitron').offsetHeight) / 2) - 21; + } + + // correct + if (mainTop < 0) { + mainTop = 0; + } + + if (document.getElementById('tarteaucitronMainLineOffset') !== null) { + if (document.getElementById('tarteaucitron').offsetHeight < (windowInnerHeight / 2)) { + mainTop -= document.getElementById('tarteaucitronMainLineOffset').offsetHeight; + } + } + + // apply + tarteaucitron.userInterface.css('tarteaucitron', 'top', mainTop + 'px'); + } + + + } else if (type === 'cookie') { + + // put cookies list at bottom + if (document.getElementById('tarteaucitronAlertSmall') !== null) { + tarteaucitron.userInterface.css('tarteaucitronCookiesListContainer', 'bottom', (document.getElementById('tarteaucitronAlertSmall').offsetHeight) + 'px'); + } + + // height of cookies list + if (document.getElementById('tarteaucitronCookiesListContainer') !== null) { + + // reset + tarteaucitron.userInterface.css('tarteaucitronCookiesList', 'height', 'auto'); + + // calculate + cookiesListHeight = document.getElementById('tarteaucitronCookiesListContainer').offsetHeight; + cookiesCloseHeight = document.getElementById('tarteaucitronClosePanelCookie').offsetHeight; + cookiesTitleHeight = document.getElementById('tarteaucitronCookiesTitle').offsetHeight; + + // apply + tarteaucitron.userInterface.css('tarteaucitronCookiesList', 'height', (cookiesListHeight - cookiesCloseHeight - cookiesTitleHeight - 2) + 'px'); + } + } + } + }, + "cookie": { + "owner": {}, + "create": function (key, status) { + "use strict"; + var d = new Date(), + time = d.getTime(), + expireTime = time + 31536000000, // 365 days + regex = new RegExp("!" + key + "=(wait|true|false)", "g"), + cookie = tarteaucitron.cookie.read().replace(regex, ""), + value = 'tarteaucitron=' + cookie + '!' + key + '=' + status; + + if (tarteaucitron.cookie.read().indexOf(key + '=' + status) === -1) { + tarteaucitron.pro('!' + key + '=' + status); + } + + d.setTime(expireTime); + document.cookie = value + '; expires=' + d.toGMTString() + '; path=/;'; + }, + "read": function () { + "use strict"; + var nameEQ = "tarteaucitron=", + ca = document.cookie.split(';'), + i, + c; + + for (i = 0; i < ca.length; i += 1) { + c = ca[i]; + while (c.charAt(0) === ' ') { + c = c.substring(1, c.length); + } + if (c.indexOf(nameEQ) === 0) { + return c.substring(nameEQ.length, c.length); + } + } + return ''; + }, + "purge": function (arr) { + "use strict"; + var i; + + for (i = 0; i < arr.length; i += 1) { + document.cookie = arr[i] + '=; expires=Thu, 01 Jan 2000 00:00:00 GMT; path=/;'; + document.cookie = arr[i] + '=; expires=Thu, 01 Jan 2000 00:00:00 GMT; path=/; domain=.' + location.hostname + ';'; + document.cookie = arr[i] + '=; expires=Thu, 01 Jan 2000 00:00:00 GMT; path=/; domain=.' + location.hostname.split('.').slice(-2).join('.') + ';'; + } + }, + "checkCount": function (key) { + "use strict"; + var arr = tarteaucitron.services[key].cookies, + nb = arr.length, + nbCurrent = 0, + html = '', + i, + status = document.cookie.indexOf(key + '=true'); + + if (status >= 0 && nb === 0) { + html += tarteaucitron.lang.useNoCookie; + } else if (status >= 0) { + for (i = 0; i < nb; i += 1) { + if (document.cookie.indexOf(arr[i] + '=') !== -1) { + nbCurrent += 1; + if (tarteaucitron.cookie.owner[arr[i]] === undefined) { + tarteaucitron.cookie.owner[arr[i]] = []; + } + if (tarteaucitron.cookie.crossIndexOf(tarteaucitron.cookie.owner[arr[i]], tarteaucitron.services[key].name) === false) { + tarteaucitron.cookie.owner[arr[i]].push(tarteaucitron.services[key].name); + } + } + } + + if (nbCurrent > 0) { + html += tarteaucitron.lang.useCookieCurrent + ' ' + nbCurrent + ' cookie'; + if (nbCurrent > 1) { + html += 's'; + } + html += '.'; + } else { + html += tarteaucitron.lang.useNoCookie; + } + } else if (nb === 0) { + html = tarteaucitron.lang.noCookie; + } else { + html += tarteaucitron.lang.useCookie + ' ' + nb + ' cookie'; + if (nb > 1) { + html += 's'; + } + html += '.'; + } + + if (document.getElementById('tacCL' + key) !== null) { + document.getElementById('tacCL' + key).innerHTML = html; + } + }, + "crossIndexOf": function (arr, match) { + "use strict"; + var i; + for (i = 0; i < arr.length; i += 1) { + if (arr[i] === match) { + return true; + } + } + return false; + }, + "number": function () { + "use strict"; + var cookies = document.cookie.split(';'), + nb = (document.cookie !== '') ? cookies.length : 0, + html = '', + i, + name, + namea, + nameb, + c, + d, + s = (nb > 1) ? 's' : '', + savedname, + regex = /^https?\:\/\/([^\/?#]+)(?:[\/?#]|$)/i, + regexedDomain = (tarteaucitron.cdn.match(regex) !== null) ? tarteaucitron.cdn.match(regex)[1] : tarteaucitron.cdn, + host = (tarteaucitron.domain !== undefined) ? tarteaucitron.domain : regexedDomain; + + cookies = cookies.sort(function (a, b) { + namea = a.split('=', 1).toString().replace(/ /g, ''); + nameb = b.split('=', 1).toString().replace(/ /g, ''); + c = (tarteaucitron.cookie.owner[namea] !== undefined) ? tarteaucitron.cookie.owner[namea] : '0'; + d = (tarteaucitron.cookie.owner[nameb] !== undefined) ? tarteaucitron.cookie.owner[nameb] : '0'; + if (c + a > d + b) { return 1; } + if (c + a < d + b) { return -1; } + return 0; + }); + + if (document.cookie !== '') { + for (i = 0; i < nb; i += 1) { + name = cookies[i].split('=', 1).toString().replace(/ /g, ''); + if (tarteaucitron.cookie.owner[name] !== undefined && tarteaucitron.cookie.owner[name].join(' // ') !== savedname) { + savedname = tarteaucitron.cookie.owner[name].join(' // '); + html += '
'; + html += '
'; + html += ' ' + tarteaucitron.cookie.owner[name].join(' // '); + html += '
'; + html += '
'; + } else if (tarteaucitron.cookie.owner[name] === undefined && host !== savedname) { + savedname = host; + html += '
'; + html += '
'; + html += ' ' + host; + html += '
'; + html += '
'; + } + html += '
'; + html += '
× ' + name + ''; + html += '
'; + html += '
' + cookies[i].split('=').slice(1).join('=') + '
'; + html += '
'; + } + } else { + html += '
'; + html += '
-
'; + html += '
'; + html += '
'; + } + + html += '
'; + + if (document.getElementById('tarteaucitronCookiesList') !== null) { + document.getElementById('tarteaucitronCookiesList').innerHTML = html; + } + + if (document.getElementById('tarteaucitronCookiesNumber') !== null) { + document.getElementById('tarteaucitronCookiesNumber').innerHTML = nb; + } + + if (document.getElementById('tarteaucitronCookiesNumberBis') !== null) { + document.getElementById('tarteaucitronCookiesNumberBis').innerHTML = nb + ' cookie' + s; + } + + for (i = 0; i < tarteaucitron.job.length; i += 1) { + tarteaucitron.cookie.checkCount(tarteaucitron.job[i]); + } + } + }, + "getLanguage": function () { + "use strict"; + if (!navigator) { return 'en'; } + + var availableLanguages = 'cs,en,fr,es,it,de,pt,pl,ru', + defaultLanguage = 'en', + lang = navigator.language || navigator.browserLanguage || + navigator.systemLanguage || navigator.userLang || null, + userLanguage = lang.substr(0, 2); + + if (tarteaucitronForceLanguage !== '') { + if (availableLanguages.indexOf(tarteaucitronForceLanguage) !== -1) { + return tarteaucitronForceLanguage; + } + } + + if (availableLanguages.indexOf(userLanguage) === -1) { + return defaultLanguage; + } + return userLanguage; + }, + "getLocale": function () { + "use strict"; + if (!navigator) { return 'en_US'; } + + var lang = navigator.language || navigator.browserLanguage || + navigator.systemLanguage || navigator.userLang || null, + userLanguage = lang.substr(0, 2); + + if (userLanguage === 'fr') { + return 'fr_FR'; + } else if (userLanguage === 'en') { + return 'en_US'; + } else if (userLanguage === 'de') { + return 'de_DE'; + } else if (userLanguage === 'es') { + return 'es_ES'; + } else if (userLanguage === 'it') { + return 'it_IT'; + } else if (userLanguage === 'pt') { + return 'pt_PT'; + } else { + return 'en_US'; + } + }, + "addScript": function (url, id, callback, execute, attrName, attrVal) { + "use strict"; + var script, + done = false; + + if (execute === false) { + if (typeof callback === 'function') { + callback(); + } + } else { + script = document.createElement('script'); + script.type = 'text/javascript'; + script.id = (id !== undefined) ? id : ''; + script.async = true; + script.src = url; + + if (attrName !== undefined && attrVal !== undefined) { + script.setAttribute(attrName, attrVal); + } + + if (typeof callback === 'function') { + script.onreadystatechange = script.onload = function () { + var state = script.readyState; + if (!done && (!state || /loaded|complete/.test(state))) { + done = true; + callback(); + } + }; + } + + document.getElementsByTagName('head')[0].appendChild(script); + } + }, + "makeAsync": { + "antiGhost": 0, + "buffer": '', + "init": function (url, id) { + "use strict"; + var savedWrite = document.write, + savedWriteln = document.writeln; + + document.write = function (content) { + tarteaucitron.makeAsync.buffer += content; + }; + document.writeln = function (content) { + tarteaucitron.makeAsync.buffer += content.concat("\n"); + }; + + setTimeout(function () { + document.write = savedWrite; + document.writeln = savedWriteln; + }, 20000); + + tarteaucitron.makeAsync.getAndParse(url, id); + }, + "getAndParse": function (url, id) { + "use strict"; + if (tarteaucitron.makeAsync.antiGhost > 9) { + tarteaucitron.makeAsync.antiGhost = 0; + return; + } + tarteaucitron.makeAsync.antiGhost += 1; + tarteaucitron.addScript(url, '', function () { + if (document.getElementById(id) !== null) { + document.getElementById(id).innerHTML += " " + tarteaucitron.makeAsync.buffer; + tarteaucitron.makeAsync.buffer = ''; + tarteaucitron.makeAsync.execJS(id); + } + }); + }, + "execJS": function (id) { + /* not strict because third party scripts may have errors */ + var i, + scripts, + childId, + type; + + if (document.getElementById(id) === null) { + return; + } + + scripts = document.getElementById(id).getElementsByTagName('script'); + for (i = 0; i < scripts.length; i += 1) { + type = (scripts[i].getAttribute('type') !== null) ? scripts[i].getAttribute('type') : ''; + if (type === '') { + type = (scripts[i].getAttribute('language') !== null) ? scripts[i].getAttribute('language') : ''; + } + if (scripts[i].getAttribute('src') !== null && scripts[i].getAttribute('src') !== '') { + childId = id + Math.floor(Math.random() * 99999999999); + document.getElementById(id).innerHTML += '
'; + tarteaucitron.makeAsync.getAndParse(scripts[i].getAttribute('src'), childId); + } else if (type.indexOf('javascript') !== -1 || type === '') { + eval(scripts[i].innerHTML); + } + } + } + }, + "fallback": function (matchClass, content, noInner) { + "use strict"; + var elems = document.getElementsByTagName('*'), + i, + index = 0; + + for (i in elems) { + if (elems[i] !== undefined) { + for (index = 0; index < matchClass.length; index += 1) { + if ((' ' + elems[i].className + ' ') + .indexOf(' ' + matchClass[index] + ' ') > -1) { + if (typeof content === 'function') { + if (noInner === true) { + content(elems[i]); + } else { + elems[i].innerHTML = content(elems[i]); + } + } else { + elems[i].innerHTML = content; + } + } + } + } + } + }, + "engage": function (id) { + "use strict"; + var html = '', + r = Math.floor(Math.random() * 100000); + + html += '
'; + html += '
'; + html += ' ' + tarteaucitron.services[id].name + ' ' + tarteaucitron.lang.fallback; + html += '
'; + html += ' ✓ ' + tarteaucitron.lang.allow; + html += '
'; + html += '
'; + html += '
'; + + return html; + }, + "extend": function (a, b) { + "use strict"; + var prop; + for (prop in b) { + if (b.hasOwnProperty(prop)) { + a[prop] = b[prop]; + } + } + }, + "proTemp": '', + "proTimer": function () { + "use strict"; + setTimeout(tarteaucitron.proPing, 1000); + }, + "pro": function (list) { + "use strict"; + tarteaucitron.proTemp += list; + clearTimeout(tarteaucitron.proTimer); + tarteaucitron.proTimer = setTimeout(tarteaucitron.proPing, 2500); + }, + "proPing": function () { + "use strict"; + if (tarteaucitron.uuid !== '' && tarteaucitron.uuid !== undefined && tarteaucitron.proTemp !== '') { + var div = document.getElementById('tarteaucitronPremium'), + timestamp = new Date().getTime(), + url = '//opt-out.ferank.eu/premium.php?'; + + if (div === null) { + return; + } + + url += 'domain=' + tarteaucitron.domain + '&'; + url += 'uuid=' + tarteaucitron.uuid + '&'; + url += 'c=' + encodeURIComponent(tarteaucitron.proTemp) + '&'; + url += '_' + timestamp; + + div.innerHTML = ''; + + tarteaucitron.proTemp = ''; + } + + tarteaucitron.cookie.number(); + } +}; diff --git a/docs/pages/assets/js/tarteaucitron/tarteaucitron.services.js b/docs/pages/assets/js/tarteaucitron/tarteaucitron.services.js new file mode 100755 index 0000000..81ea5c7 --- /dev/null +++ b/docs/pages/assets/js/tarteaucitron/tarteaucitron.services.js @@ -0,0 +1,1850 @@ +/*global tarteaucitron, ga, Shareaholic, stLight, clicky, top, google, Typekit, FB, ferankReady, IN, stButtons, twttr, PCWidget*/ +/*jslint regexp: true, nomen: true*/ + +// generic iframe +tarteaucitron.services.iframe = { + "key": "iframe", + "type": "other", + "name": "Web content", + "uri": "", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + tarteaucitron.fallback(['tac_iframe'], function (x) { + var width = x.getAttribute("width"), + height = x.getAttribute("height"), + url = x.getAttribute("data-url"); + + return ''; + }); + }, + "fallback": function () { + "use strict"; + var id = 'iframe'; + tarteaucitron.fallback(['tac_iframe'], function (elem) { + elem.style.width = elem.getAttribute('width') + 'px'; + elem.style.height = elem.getAttribute('height') + 'px'; + return tarteaucitron.engage(id); + }); + } +}; + +// addthis +tarteaucitron.services.addthis = { + "key": "addthis", + "type": "social", + "name": "AddThis", + "uri": "https://www.addthis.com/privacy/privacy-policy#publisher-visitors", + "needConsent": true, + "cookies": ['__atuvc', '__atuvs'], + "js": function () { + "use strict"; + if (tarteaucitron.user.addthisPubId === undefined) { + return; + } + if (tarteaucitron.isAjax === true) { + window.addthis = null; + window._adr = null; + window._atc = null; + window._atd = null; + window._ate = null; + window._atr = null; + window._atw = null; + } + tarteaucitron.fallback(['addthis_sharing_toolbox'], ''); + tarteaucitron.addScript('//s7.addthis.com/js/300/addthis_widget.js#pubid=' + tarteaucitron.user.addthisPubId); + }, + "fallback": function () { + "use strict"; + var id = 'addthis'; + tarteaucitron.fallback(['addthis_sharing_toolbox'], tarteaucitron.engage(id)); + } +}; + +// addtoanyfeed +tarteaucitron.services.addtoanyfeed = { + "key": "addtoanyfeed", + "type": "social", + "name": "AddToAny (feed)", + "uri": "https://www.addtoany.com/privacy", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + if (tarteaucitron.user.addtoanyfeedUri === undefined) { + return; + } + tarteaucitron.user.addtoanyfeedSubscribeLink = 'https://www.addtoany.com/subscribe?linkurl=' + tarteaucitron.user.addtoanyfeedUri; + window.a2a_config = window.a2a_config || {}; + window.a2a_config.linkurl = tarteaucitron.user.addtoanyfeedUri; + tarteaucitron.addScript('//static.addtoany.com/menu/feed.js'); + }, + "fallback": function () { + "use strict"; + tarteaucitron.user.addtoanyfeedSubscribeLink = 'https://www.addtoany.com/subscribe?linkurl=' + tarteaucitron.user.addtoanyfeedUri; + } +}; + +// addtoanyshare +tarteaucitron.services.addtoanyshare = { + "key": "addtoanyshare", + "type": "social", + "name": "AddToAny (share)", + "uri": "https://www.addtoany.com/privacy", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + tarteaucitron.fallback(['tac_addtoanyshare'], ''); + tarteaucitron.addScript('//static.addtoany.com/menu/page.js'); + }, + "fallback": function () { + "use strict"; + var id = 'addtoanyshare'; + tarteaucitron.fallback(['tac_addtoanyshare'], tarteaucitron.engage(id)); + } +}; + +// alexa +tarteaucitron.services.alexa = { + "key": "alexa", + "type": "analytic", + "name": "Alexa", + "uri": "https://www.alexa.com/help/privacy", + "needConsent": true, + "cookies": ['__asc', '__auc'], + "js": function () { + "use strict"; + if (tarteaucitron.user.alexaAccountID === undefined) { + return; + } + window._atrk_opts = { + atrk_acct: tarteaucitron.user.alexaAccountID, + domain: window.location.hostname.match(/[^\.]*\.[^.]*$/)[0], + dynamic: true + }; + tarteaucitron.addScript('https://d31qbv1cthcecs.cloudfront.net/atrk.js'); + } +}; + +// amazon +tarteaucitron.services.amazon = { + "key": "amazon", + "type": "ads", + "name": "Amazon", + "uri": "https://www.amazon.fr/gp/help/customer/display.html?ie=UTF8&*Version*=1&*entries*=0&nodeId=201149360", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + tarteaucitron.fallback(['amazon_product'], function (x) { + var amazonId = x.getAttribute("amazonid"), + productId = x.getAttribute("productid"), + url = '//ws-eu.amazon-adsystem.com/widgets/q?ServiceVersion=20070822&OneJS=1&Operation=GetAdHtml&MarketPlace=' + tarteaucitron.getLanguage().toUpperCase() + '&source=ss&ref=ss_til&ad_type=product_link&tracking_id=' + amazonId + '&marketplace=amazon®ion=' + tarteaucitron.getLanguage().toUpperCase() + '&placement=' + productId + '&asins=' + productId + '&show_border=true&link_opens_in_new_window=true', + iframe = ''; + + return iframe; + }); + }, + "fallback": function () { + "use strict"; + var id = 'amazon'; + tarteaucitron.fallback(['amazon_product'], tarteaucitron.engage(id)); + } +}; + +// calameo +tarteaucitron.services.calameo = { + "key": "calameo", + "type": "video", + "name": "Calameo", + "uri": "https://fr.calameo.com/privacy", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + tarteaucitron.fallback(['calameo-canvas'], function (x) { + var id = x.getAttribute("data-id"), + width = x.getAttribute("width"), + height = x.getAttribute("height"), + url = '//v.calameo.com/?bkcode=' + id; + + return ''; + }); + }, + "fallback": function () { + "use strict"; + var id = 'calameo'; + tarteaucitron.fallback(['calameo-canvas'], function (elem) { + elem.style.width = elem.getAttribute('width') + 'px'; + elem.style.height = elem.getAttribute('height') + 'px'; + return tarteaucitron.engage(id); + }); + } +}; + +// clicky +tarteaucitron.services.clicky = { + "key": "clicky", + "type": "analytic", + "name": "Clicky", + "uri": "https://clicky.com/terms", + "needConsent": true, + "cookies": ['_jsuid', '_eventqueue', '_referrer_og', '_utm_og', '_first_pageview', 'clicky_olark', 'no_trackyy_' + tarteaucitron.user.clickyId, 'unpoco_' + tarteaucitron.user.clickyId, 'heatmaps_g2g_' + tarteaucitron.user.clickyId], + "js": function () { + "use strict"; + if (tarteaucitron.user.clickyId === undefined) { + return; + } + tarteaucitron.addScript('//static.getclicky.com/js', '', function () { + if (typeof clicky.init === 'function') { + clicky.init(tarteaucitron.user.clickyId); + } + if (typeof tarteaucitron.user.clickyMore === 'function') { + tarteaucitron.user.clickyMore(); + } + }); + } +}; + +// clicmanager +tarteaucitron.services.clicmanager = { + "key": "clicmanager", + "type": "ads", + "name": "Clicmanager", + "uri": "http://www.clicmanager.fr/infos_legales.php", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + var uniqIds = [], + i, + uri; + + tarteaucitron.fallback(['clicmanager-canvas'], function (x) { + var uniqId = '_' + Math.random().toString(36).substr(2, 9); + uniqIds.push(uniqId); + return '
'; + }); + + for (i = 0; i < uniqIds.length; i += 1) { + uri = '//ads.clicmanager.fr/exe.php?'; + uri += 'c=' + document.getElementById(uniqIds[i]).getAttribute('c') + '&'; + uri += 's=' + document.getElementById(uniqIds[i]).getAttribute('s') + '&'; + uri += 't=' + document.getElementById(uniqIds[i]).getAttribute('t'); + + tarteaucitron.makeAsync.init(uri, uniqIds[i]); + } + }, + "fallback": function () { + "use strict"; + var id = 'clicmanager'; + tarteaucitron.fallback(['clicmanager-canvas'], tarteaucitron.engage(id)); + } +}; + +// crazyegg +tarteaucitron.services.crazyegg = { + "key": "crazyegg", + "type": "analytic", + "name": "Crazy Egg", + "uri": "https://www.crazyegg.com/privacy", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + + if (tarteaucitron.user.crazyeggId === undefined) { + return; + } + + tarteaucitron.addScript('//script.crazyegg.com/pages/scripts/' + tarteaucitron.user.crazyeggId.substr(0, 4) + '/' + tarteaucitron.user.crazyeggId.substr(4, 4) + '.js'); + } +}; + +// criteo +tarteaucitron.services.criteo = { + "key": "criteo", + "type": "ads", + "name": "Criteo", + "uri": "http://www.criteo.com/privacy/", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + document.MAX_ct0 = ''; + var uniqIds = [], + i, + uri; + + tarteaucitron.fallback(['criteo-canvas'], function (x) { + var uniqId = '_' + Math.random().toString(36).substr(2, 9); + uniqIds.push(uniqId); + return '
'; + }); + + for (i = 0; i < uniqIds.length; i += 1) { + uri = '//cas.criteo.com/delivery/ajs.php?'; + uri += 'zoneid=' + document.getElementById(uniqIds[i]).getAttribute('zoneid'); + uri += '&nodis=1&cb=' + Math.floor(Math.random() * 99999999999); + uri += '&loc=' + encodeURI(window.location); + uri += (document.MAX_used !== ',') ? '&exclude=' + document.MAX_used : ''; + uri += (document.charset !== undefined ? '&charset=' + document.charset : ''); + uri += (document.characterSet !== undefined ? '&charset=' + document.characterSet : ''); + uri += (document.referrer !== undefined) ? '&referer=' + encodeURI(document.referrer) : ''; + uri += (document.context !== undefined) ? '&context=' + encodeURI(document.context) : ''; + uri += ((document.MAX_ct0 !== undefined) && (document.MAX_ct0.substring(0, 4) === 'http')) ? '&ct0=' + encodeURI(document.MAX_ct0) : ''; + uri += (document.mmm_fo !== undefined) ? '&mmm_fo=1' : ''; + + tarteaucitron.makeAsync.init(uri, uniqIds[i]); + } + }, + "fallback": function () { + "use strict"; + var id = 'criteo'; + tarteaucitron.fallback(['criteo-canvas'], tarteaucitron.engage(id)); + } +}; + +// dailymotion +tarteaucitron.services.dailymotion = { + "key": "dailymotion", + "type": "video", + "name": "Dailymotion", + "uri": "https://www.dailymotion.com/legal/privacy", + "needConsent": true, + "cookies": ['ts', 'dmvk', 'hist', 'v1st', 's_vi'], + "js": function () { + "use strict"; + tarteaucitron.fallback(['dailymotion_player'], function (x) { + var video_id = x.getAttribute("videoID"), + video_width = x.getAttribute("width"), + frame_width = 'width=', + video_height = x.getAttribute("height"), + frame_height = 'height=', + video_frame, + params = 'info=' + x.getAttribute("showinfo") + '&autoPlay=' + x.getAttribute("autoplay"); + + if (video_id === undefined) { + return ""; + } + if (video_width !== undefined) { + frame_width += '"' + video_width + '" '; + } else { + frame_width += '"" '; + } + if (video_height !== undefined) { + frame_height += '"' + video_height + '" '; + } else { + frame_height += '"" '; + } + video_frame = ''; + return video_frame; + }); + }, + "fallback": function () { + "use strict"; + var id = 'dailymotion'; + tarteaucitron.fallback(['dailymotion_player'], function (elem) { + elem.style.width = elem.getAttribute('width') + 'px'; + elem.style.height = elem.getAttribute('height') + 'px'; + return tarteaucitron.engage(id); + }); + } +}; + +// dating affiliation +tarteaucitron.services.datingaffiliation = { + "key": "datingaffiliation", + "type": "ads", + "name": "Dating Affiliation", + "uri": "http://www.dating-affiliation.com/conditions-generales.php", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + tarteaucitron.fallback(['datingaffiliation-canvas'], function (x) { + var comfrom = x.getAttribute("data-comfrom"), + r = x.getAttribute("data-r"), + p = x.getAttribute("data-p"), + cf0 = x.getAttribute("data-cf0"), + langue = x.getAttribute("data-langue"), + forward_affiliate = x.getAttribute("data-forwardAffiliate"), + cf2 = x.getAttribute("data-cf2"), + cfsa2 = x.getAttribute("data-cfsa2"), + width = x.getAttribute("width"), + height = x.getAttribute("height"), + url = 'http://www.tools-affil2.com/rotaban/ban.php?' + comfrom; + + return ''; + }); + }, + "fallback": function () { + "use strict"; + var id = 'datingaffiliation'; + tarteaucitron.fallback(['datingaffiliation-canvas'], function (elem) { + elem.style.width = elem.getAttribute('width') + 'px'; + elem.style.height = elem.getAttribute('height') + 'px'; + return tarteaucitron.engage(id); + }); + } +}; + +// dating affiliation popup +tarteaucitron.services.datingaffiliationpopup = { + "key": "datingaffiliationpopup", + "type": "ads", + "name": "Dating Affiliation (Pop Up)", + "uri": "http://www.dating-affiliation.com/conditions-generales.php", + "needConsent": true, + "cookies": ['__utma', '__utmb', '__utmc', '__utmt_Tools', '__utmv', '__utmz', '_ga', '_gat', '_gat_UA-65072040-17', '__da-pu-xflirt-ID-pc-o169'], + "js": function () { + "use strict"; + var uniqIds = [], + i, + uri; + + tarteaucitron.fallback(['datingaffiliationpopup-canvas'], function (x) { + var uniqId = '_' + Math.random().toString(36).substr(2, 9); + uniqIds.push(uniqId); + return '
'; + }); + + for (i = 0; i < uniqIds.length; i += 1) { + uri = 'http://www.promotools.biz/da/popunder/script.php?'; + uri += 'comfrom=' + document.getElementById(uniqIds[i]).getAttribute('comfrom') + '&'; + uri += 'promo=' + document.getElementById(uniqIds[i]).getAttribute('promo') + '&'; + uri += 'product_id=' + document.getElementById(uniqIds[i]).getAttribute('productid') + '&'; + uri += 'submitconfig=' + document.getElementById(uniqIds[i]).getAttribute('submitconfig') + '&'; + uri += 'ur=' + document.getElementById(uniqIds[i]).getAttribute('ur') + '&'; + uri += 'brand=' + document.getElementById(uniqIds[i]).getAttribute('brand') + '&'; + uri += 'lang=' + document.getElementById(uniqIds[i]).getAttribute('lang') + '&'; + uri += 'cf0=' + document.getElementById(uniqIds[i]).getAttribute('cf0') + '&'; + uri += 'cf2=' + document.getElementById(uniqIds[i]).getAttribute('cf2') + '&'; + uri += 'subid1=' + document.getElementById(uniqIds[i]).getAttribute('subid1') + '&'; + uri += 'cfsa2=' + document.getElementById(uniqIds[i]).getAttribute('cfsa2') + '&'; + uri += 'subid2=' + document.getElementById(uniqIds[i]).getAttribute('subid2') + '&'; + uri += 'nicheId=' + document.getElementById(uniqIds[i]).getAttribute('nicheid') + '&'; + uri += 'degreId=' + document.getElementById(uniqIds[i]).getAttribute('degreid') + '&'; + uri += 'bt=' + document.getElementById(uniqIds[i]).getAttribute('bt') + '&'; + uri += 'vis=' + document.getElementById(uniqIds[i]).getAttribute('vis') + '&'; + uri += 'hid=' + document.getElementById(uniqIds[i]).getAttribute('hid') + '&'; + uri += 'snd=' + document.getElementById(uniqIds[i]).getAttribute('snd') + '&'; + uri += 'aabd=' + document.getElementById(uniqIds[i]).getAttribute('aabd') + '&'; + uri += 'aabs=' + document.getElementById(uniqIds[i]).getAttribute('aabs'); + + tarteaucitron.makeAsync.init(uri, uniqIds[i]); + } + }, + "fallback": function () { + "use strict"; + var id = 'datingaffiliationpopup'; + tarteaucitron.fallback(['datingaffiliationpopup-canvas'], tarteaucitron.engage(id)); + } +}; + +// disqus +tarteaucitron.services.disqus = { + "key": "disqus", + "type": "comment", + "name": "Disqus", + "uri": "https://help.disqus.com/customer/portal/articles/466259-privacy-policy", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + if (tarteaucitron.user.disqusShortname === undefined) { + return; + } + tarteaucitron.addScript('//' + tarteaucitron.user.disqusShortname + '.disqus.com/embed.js'); + tarteaucitron.addScript('//' + tarteaucitron.user.disqusShortname + '.disqus.com/count.js'); + }, + "fallback": function () { + "use strict"; + var id = 'disqus'; + + if (document.getElementById('disqus_thread')) { + document.getElementById('disqus_thread').innerHTML = tarteaucitron.engage(id); + } + } +}; + +// ekomi +tarteaucitron.services.ekomi = { + "key": "ekomi", + "type": "social", + "name": "eKomi", + "uri": "http://www.ekomi-us.com/us/privacy/", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + if (tarteaucitron.user.ekomiCertId === undefined) { + return; + } + window.eKomiIntegrationConfig = [ + {certId: tarteaucitron.user.ekomiCertId} + ]; + tarteaucitron.addScript('//connect.ekomi.de/integration_1410173009/' + tarteaucitron.user.ekomiCertId + '.js'); + } +}; + +// etracker +tarteaucitron.services.etracker = { + "key": "etracker", + "type": "analytic", + "name": "eTracker", + "uri": "https://www.etracker.com/en/data-protection.html", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + if (tarteaucitron.user.etracker === undefined) { + return; + } + + tarteaucitron.addScript('//static.etracker.com/code/e.js', '_etLoader', function () {}, true, "data-secure-code", tarteaucitron.user.etracker); + } +}; + +// facebook +tarteaucitron.services.facebook = { + "key": "facebook", + "type": "social", + "name": "Facebook", + "uri": "https://www.facebook.com/policies/cookies/", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + tarteaucitron.fallback(['fb-post', 'fb-follow', 'fb-activity', 'fb-send', 'fb-share-button', 'fb-like'], ''); + tarteaucitron.addScript('//connect.facebook.net/' + tarteaucitron.getLocale() + '/sdk.js#xfbml=1&version=v2.0', 'facebook-jssdk'); + if (tarteaucitron.isAjax === true) { + if (typeof FB !== "undefined") { + FB.XFBML.parse(); + } + } + }, + "fallback": function () { + "use strict"; + var id = 'facebook'; + tarteaucitron.fallback(['fb-post', 'fb-follow', 'fb-activity', 'fb-send', 'fb-share-button', 'fb-like'], tarteaucitron.engage(id)); + } +}; + +// facebooklikebox +tarteaucitron.services.facebooklikebox = { + "key": "facebooklikebox", + "type": "social", + "name": "Facebook (like box)", + "uri": "https://www.facebook.com/policies/cookies/", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + tarteaucitron.fallback(['fb-like-box', 'fb-page'], ''); + tarteaucitron.addScript('//connect.facebook.net/' + tarteaucitron.getLocale() + '/sdk.js#xfbml=1&version=v2.3', 'facebook-jssdk'); + if (tarteaucitron.isAjax === true) { + if (typeof FB !== "undefined") { + FB.XFBML.parse(); + } + } + }, + "fallback": function () { + "use strict"; + var id = 'facebooklikebox'; + tarteaucitron.fallback(['fb-like-box', 'fb-page'], tarteaucitron.engage(id)); + } +}; + +// facebookcomment +tarteaucitron.services.facebookcomment = { + "key": "facebookcomment", + "type": "comment", + "name": "Facebook (commentaire)", + "uri": "https://www.facebook.com/policies/cookies/", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + tarteaucitron.fallback(['fb-comments'], ''); + tarteaucitron.addScript('//connect.facebook.net/' + tarteaucitron.getLocale() + '/sdk.js#xfbml=1&version=v2.0', 'facebook-jssdk'); + if (tarteaucitron.isAjax === true) { + if (typeof FB !== "undefined") { + FB.XFBML.parse(); + } + } + }, + "fallback": function () { + "use strict"; + var id = 'facebookcomment'; + tarteaucitron.fallback(['fb-comments'], tarteaucitron.engage(id)); + } +}; + +// ferank +tarteaucitron.services.ferank = { + "key": "ferank", + "type": "analytic", + "name": "FERank", + "uri": "https://www.ferank.fr/respect-vie-privee/#mesureaudience", + "needConsent": false, + "cookies": [], + "js": function () { + "use strict"; + tarteaucitron.addScript('//static.ferank.fr/pixel.js', '', function () { + if (typeof tarteaucitron.user.ferankMore === 'function') { + tarteaucitron.user.ferankMore(); + } + }); + } +}; + +// ferank pub +tarteaucitron.services.ferankpub = { + "key": "ferankpub", + "type": "ads", + "name": "FERank (pub)", + "uri": "https://www.ferank.fr/respect-vie-privee/#regiepublicitaire", + "needConsent": false, + "cookies": [], + "js": function () { + "use strict"; + tarteaucitron.addScript('//static.ferank.fr/publicite.async.js'); + if (tarteaucitron.isAjax === true) { + if (typeof ferankReady === 'function') { + ferankReady(); + } + } + }, + "fallback": function () { + "use strict"; + var id = 'ferankpub'; + tarteaucitron.fallback(['ferank-publicite'], tarteaucitron.engage(id)); + } +}; + +// get+ +tarteaucitron.services.getplus = { + "key": "getplus", + "type": "analytic", + "name": "Get+", + "uri": "http://www.getplus.fr/Conditions-generales-de-vente_a226.html", + "needConsent": true, + "cookies": ['_first_pageview', '_jsuid', 'no_trackyy_' + tarteaucitron.user.getplusId, '_eventqueue'], + "js": function () { + "use strict"; + if (tarteaucitron.user.getplusId === undefined) { + return; + } + + window.webleads_site_ids = window.webleads_site_ids || []; + window.webleads_site_ids.push(tarteaucitron.user.getplusId); + tarteaucitron.addScript('//stats.webleads-tracker.com/js'); + } +}; + +// google+ +tarteaucitron.services.gplus = { + "key": "gplus", + "type": "social", + "name": "Google+", + "uri": "https://policies.google.com/privacy", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + tarteaucitron.addScript('https://apis.google.com/js/platform.js'); + }, + "fallback": function () { + "use strict"; + var id = 'gplus'; + tarteaucitron.fallback(['g-plus', 'g-plusone'], tarteaucitron.engage(id)); + } +}; + +// google+ badge +tarteaucitron.services.gplusbadge = { + "key": "gplusbadge", + "type": "social", + "name": "Google+ (badge)", + "uri": "https://policies.google.com/privacy", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + tarteaucitron.addScript('https://apis.google.com/js/platform.js'); + }, + "fallback": function () { + "use strict"; + var id = 'gplusbadge'; + tarteaucitron.fallback(['g-page', 'g-person'], tarteaucitron.engage(id)); + } +}; + +// google adsense +tarteaucitron.services.adsense = { + "key": "adsense", + "type": "ads", + "name": "Google Adsense", + "uri": "http://www.google.com/ads/preferences/", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + tarteaucitron.addScript('https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js'); + }, + "fallback": function () { + "use strict"; + var id = 'adsense'; + tarteaucitron.fallback(['adsbygoogle'], tarteaucitron.engage(id)); + } +}; + +// google partners badge +tarteaucitron.services.googlepartners = { + "key": "googlepartners", + "type": "ads", + "name": "Google Partners Badge", + "uri": "http://www.google.com/ads/preferences/", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + tarteaucitron.addScript('https://apis.google.com/js/platform.js'); + }, + "fallback": function () { + "use strict"; + var id = 'googlepartners'; + tarteaucitron.fallback(['g-partnersbadge'], tarteaucitron.engage(id)); + } +}; + +// google adsense search (form) +tarteaucitron.services.adsensesearchform = { + "key": "adsensesearchform", + "type": "ads", + "name": "Google Adsense Search (form)", + "uri": "http://www.google.com/ads/preferences/", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + tarteaucitron.addScript('//www.google.com/coop/cse/brand?form=cse-search-box&lang=' + tarteaucitron.getLanguage()); + } +}; + +// google adsense search (result) +tarteaucitron.services.adsensesearchresult = { + "key": "adsensesearchresult", + "type": "ads", + "name": "Google Adsense Search (result)", + "uri": "http://www.google.com/ads/preferences/", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + if (tarteaucitron.user.adsensesearchresultCx === undefined) { + return; + } + tarteaucitron.addScript('//www.google.com/cse/cse.js?cx=' + tarteaucitron.user.adsensesearchresultCx); + }, + "fallback": function () { + "use strict"; + var id = 'adsensesearchresult'; + + if (document.getElementById('gcse_searchresults')) { + document.getElementById('gcse_searchresults').innerHTML = tarteaucitron.engage(id); + } + } +}; + +// googleadwordsconversion +tarteaucitron.services.googleadwordsconversion = { + "key": "googleadwordsconversion", + "type": "ads", + "name": "Google Adwords (conversion)", + "uri": "https://www.google.com/settings/ads", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + if (tarteaucitron.user.adwordsconversionId === undefined) { + return; + } + + tarteaucitron.addScript('//www.googleadservices.com/pagead/conversion_async.js', '', function () { + window.google_trackConversion({ + google_conversion_id: tarteaucitron.user.adwordsconversionId, + google_conversion_label: tarteaucitron.user.adwordsconversionLabel, + google_conversion_language: tarteaucitron.user.adwordsconversionLanguage, + google_conversion_format: tarteaucitron.user.adwordsconversionFormat, + google_conversion_color: tarteaucitron.user.adwordsconversionColor, + google_conversion_value: tarteaucitron.user.adwordsconversionValue, + google_conversion_currency: tarteaucitron.user.adwordsconversionCurrency, + google_custom_params: { + parameter1: tarteaucitron.user.adwordsconversionCustom1, + parameter2: tarteaucitron.user.adwordsconversionCustom2 + } + }); + }); + } +}; + +// googleadwordsremarketing +tarteaucitron.services.googleadwordsremarketing = { + "key": "googleadwordsremarketing", + "type": "ads", + "name": "Google Adwords (remarketing)", + "uri": "https://www.google.com/settings/ads", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + if (tarteaucitron.user.adwordsremarketingId === undefined) { + return; + } + + tarteaucitron.addScript('//www.googleadservices.com/pagead/conversion_async.js', '', function () { + window.google_trackConversion({ + google_conversion_id: tarteaucitron.user.adwordsremarketingId, + google_remarketing_only: true + }); + }); + } +}; + +// google analytics (old) +tarteaucitron.services.gajs = { + "key": "gajs", + "type": "analytic", + "name": "Google Analytics (ga.js)", + "uri": "https://support.google.com/analytics/answer/6004245", + "needConsent": true, + "cookies": ['_ga', '_gat', '__utma', '__utmb', '__utmc', '__utmt', '__utmz'], + "js": function () { + "use strict"; + window._gaq = window._gaq || []; + window._gaq.push(['_setAccount', tarteaucitron.user.gajsUa]); + window._gaq.push(['_trackPageview']); + + tarteaucitron.addScript('//www.google-analytics.com/ga.js', '', function () { + if (typeof tarteaucitron.user.gajsMore === 'function') { + tarteaucitron.user.gajsMore(); + } + }); + } +}; + +// google analytics +tarteaucitron.services.analytics = { + "key": "analytics", + "type": "analytic", + "name": "Google Analytics (universal)", + "uri": "https://support.google.com/analytics/answer/6004245", + "needConsent": true, + "cookies": ['_ga', '_gat', '_gid', '__utma', '__utmb', '__utmc', '__utmt', '__utmz'], + "js": function () { + "use strict"; + window.GoogleAnalyticsObject = 'ga'; + window.ga = window.ga || function () { + window.ga.q = window.ga.q || []; + window.ga.q.push(arguments); + }; + window.ga.l = new Date(); + + tarteaucitron.addScript('//www.google-analytics.com/analytics.js', '', function () { + ga('create', tarteaucitron.user.analyticsUa, {'cookieExpires': 34128000}); + ga('send', 'pageview'); + if (typeof tarteaucitron.user.analyticsMore === 'function') { + tarteaucitron.user.analyticsMore(); + } + }); + } +}; + +// google analytics +tarteaucitron.services.gtag = { + "key": "gtag", + "type": "analytic", + "name": "Google Analytics (gtag.js)", + "uri": "https://support.google.com/analytics/answer/6004245", + "needConsent": true, + "cookies": ['_ga', '_gat', '_gid', '__utma', '__utmb', '__utmc', '__utmt', '__utmz'], + "js": function () { + "use strict"; + window.dataLayer = window.dataLayer || []; + + tarteaucitron.addScript('//www.googletagmanager.com/gtag/js?id=' + tarteaucitron.user.gtagUa, '', function () { + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + gtag('config', tarteaucitron.user.gtagUa); + + if (typeof tarteaucitron.user.gtagMore === 'function') { + tarteaucitron.user.gtagMore(); + } + }); + } +}; + +// google maps +tarteaucitron.services.googlemaps = { + "key": "googlemaps", + "type": "api", + "name": "Google Maps", + "uri": "http://www.google.com/ads/preferences/", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + var mapOptions, + map, + uniqIds = [], + i; + + if (tarteaucitron.user.mapscallback === undefined) { + tarteaucitron.user.mapscallback = 'tac_googlemaps_callback'; + } + + tarteaucitron.addScript('//maps.googleapis.com/maps/api/js?v=3.exp&key=' + tarteaucitron.user.googlemapsKey + '&callback='+tarteaucitron.user.mapscallback); + + window.tac_googlemaps_callback = function () { + tarteaucitron.fallback(['googlemaps-canvas'], function (x) { + var uniqId = '_' + Math.random().toString(36).substr(2, 9); + uniqIds.push(uniqId); + return '
'; + }); + + for (i = 0; i < uniqIds.length; i += 1) { + mapOptions = { + zoom: parseInt(document.getElementById(uniqIds[i]).getAttribute('zoom'), 10), + center: new google.maps.LatLng(parseFloat(document.getElementById(uniqIds[i]).getAttribute('latitude'), 10), parseFloat(document.getElementById(uniqIds[i]).getAttribute('longitude'), 10)) + }; + map = new google.maps.Map(document.getElementById(uniqIds[i]), mapOptions); + } + }; + }, + "fallback": function () { + "use strict"; + var id = 'googlemaps'; + tarteaucitron.fallback(['googlemaps-canvas'], tarteaucitron.engage(id)); + } +}; + +// google tag manager +tarteaucitron.services.googletagmanager = { + "key": "googletagmanager", + "type": "api", + "name": "Google Tag Manager", + "uri": "http://www.google.com/ads/preferences/", + "needConsent": true, + "cookies": ['_ga', '_gat', '__utma', '__utmb', '__utmc', '__utmt', '__utmz', '__gads', '_drt_', 'FLC', 'exchange_uid', 'id', 'fc', 'rrs', 'rds', 'rv', 'uid', 'UIDR', 'UID', 'clid', 'ipinfo', 'acs'], + "js": function () { + "use strict"; + if (tarteaucitron.user.googletagmanagerId === undefined) { + return; + } + window.dataLayer = window.dataLayer || []; + window.dataLayer.push({ + 'gtm.start': new Date().getTime(), + event: 'gtm.js' + }); + tarteaucitron.addScript('//www.googletagmanager.com/gtm.js?id=' + tarteaucitron.user.googletagmanagerId); + } +}; + +// jsapi +tarteaucitron.services.jsapi = { + "key": "jsapi", + "type": "api", + "name": "Google jsapi", + "uri": "http://www.google.com/policies/privacy/", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + tarteaucitron.addScript('//www.google.com/jsapi'); + } +}; + +// linkedin +tarteaucitron.services.linkedin = { + "key": "linkedin", + "type": "social", + "name": "Linkedin", + "uri": "https://www.linkedin.com/legal/cookie_policy", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + tarteaucitron.fallback(['tacLinkedin'], ''); + tarteaucitron.addScript('//platform.linkedin.com/in.js'); + if (tarteaucitron.isAjax === true) { + if (typeof IN !== "undefined") { + IN.parse(); + } + } + }, + "fallback": function () { + "use strict"; + var id = 'linkedin'; + tarteaucitron.fallback(['tacLinkedin'], tarteaucitron.engage(id)); + } +}; + +// mautic +tarteaucitron.services.mautic = { + "key": "mautic", + "type": "analytic", + "name": "Mautic", + "uri": "https://www.mautic.org/privacy-policy/", + "needConsent": true, + "cookies": ['mtc_id', 'mtc_sid'], + "js": function () { + "use strict"; + if (tarteaucitron.user.mauticurl === undefined) { + return; + } + + window['MauticTrackingObject'] = 'mt'; + window['mt'] = window['mt'] || function() { + (window['mt'].q = window['mt'].q || []).push(arguments); + }; + + tarteaucitron.addScript(tarteaucitron.user.mauticurl, '', function() { + mt('send', 'pageview'); + }); + } +}; + +// microsoftcampaignanalytics +tarteaucitron.services.microsoftcampaignanalytics = { + "key": "microsoftcampaignanalytics", + "type": "analytic", + "name": "Microsoft Campaign Analytics", + "uri": "https://privacy.microsoft.com/privacystatement/", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + if (tarteaucitron.user.microsoftcampaignanalyticsUUID === undefined) { + return; + } + + tarteaucitron.addScript('//flex.atdmt.com/mstag/site/' + tarteaucitron.user.microsoftcampaignanalyticsUUID + '/mstag.js', 'mstag_tops', function () { + window.mstag = {loadTag : function () {}, time : (new Date()).getTime()}; + window.mstag.loadTag("analytics", {dedup: "1", domainId: tarteaucitron.user.microsoftcampaignanalyticsdomainId, type: "1", actionid: tarteaucitron.user.microsoftcampaignanalyticsactionId}); + }); + } +}; + +// pinterest +tarteaucitron.services.pinterest = { + "key": "pinterest", + "type": "social", + "name": "Pinterest", + "uri": "https://about.pinterest.com/privacy-policy", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + tarteaucitron.fallback(['tacPinterest'], ''); + tarteaucitron.addScript('//assets.pinterest.com/js/pinit.js'); + }, + "fallback": function () { + "use strict"; + var id = 'pinterest'; + tarteaucitron.fallback(['tacPinterest'], tarteaucitron.engage(id)); + } +}; + +// prelinker +tarteaucitron.services.prelinker = { + "key": "prelinker", + "type": "ads", + "name": "Prelinker", + "uri": "http://www.prelinker.com/index/index/cgu/", + "needConsent": true, + "cookies": ['_sp_id.32f5', '_sp_ses.32f5'], + "js": function () { + "use strict"; + var uniqIds = [], + i, + uri; + + tarteaucitron.fallback(['prelinker-canvas'], function (x) { + var uniqId = '_' + Math.random().toString(36).substr(2, 9); + uniqIds.push(uniqId); + return '
'; + }); + + for (i = 0; i < uniqIds.length; i += 1) { + uri = 'http://promo.easy-dating.org/banner/index?'; + uri += 'site_id=' + document.getElementById(uniqIds[i]).getAttribute('siteId') + '&'; + uri += 'banner_id=' + document.getElementById(uniqIds[i]).getAttribute('bannerId') + '&'; + uri += 'default_language=' + document.getElementById(uniqIds[i]).getAttribute('defaultLanguage') + '&'; + uri += 'tr4ck=' + document.getElementById(uniqIds[i]).getAttribute('trackrt'); + + tarteaucitron.makeAsync.init(uri, uniqIds[i]); + } + }, + "fallback": function () { + "use strict"; + var id = 'prelinker'; + tarteaucitron.fallback(['prelinker-canvas'], tarteaucitron.engage(id)); + } +}; + +// prezi +tarteaucitron.services.prezi = { + "key": "prezi", + "type": "video", + "name": "Prezi", + "uri": "https://prezi.com/privacy-policy/", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + tarteaucitron.fallback(['prezi-canvas'], function (x) { + var id = x.getAttribute("data-id"), + width = x.getAttribute("width"), + height = x.getAttribute("height"), + url = 'https://prezi.com/embed/' + id + '/?bgcolor=ffffff&lock_to_path=0&autoplay=0&autohide_ctrls=0'; + + return ''; + }); + }, + "fallback": function () { + "use strict"; + var id = 'prezi'; + tarteaucitron.fallback(['prezi-canvas'], function (elem) { + elem.style.width = elem.getAttribute('width') + 'px'; + elem.style.height = elem.getAttribute('height') + 'px'; + return tarteaucitron.engage(id); + }); + } +}; + +// pubdirecte +tarteaucitron.services.pubdirecte = { + "key": "pubdirecte", + "type": "ads", + "name": "Pubdirecte", + "uri": "http://pubdirecte.com/contact.php", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + var uniqIds = [], + i, + uri; + + tarteaucitron.fallback(['pubdirecte-canvas'], function (x) { + var uniqId = '_' + Math.random().toString(36).substr(2, 9); + uniqIds.push(uniqId); + return '
'; + }); + + for (i = 0; i < uniqIds.length; i += 1) { + uri = '//www.pubdirecte.com/script/banniere.php?'; + uri += 'id=' + document.getElementById(uniqIds[i]).getAttribute('pid') + '&'; + uri += 'ref=' + document.getElementById(uniqIds[i]).getAttribute('ref'); + + tarteaucitron.makeAsync.init(uri, uniqIds[i]); + } + }, + "fallback": function () { + "use strict"; + var id = 'pubdirecte'; + tarteaucitron.fallback(['pubdirecte-canvas'], tarteaucitron.engage(id)); + } +}; + +// purechat +tarteaucitron.services.purechat = { + "key": "purechat", + "type": "support", + "name": "PureChat", + "uri": "https://www.purechat.com/privacy", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + if (tarteaucitron.user.purechatId === undefined) { + return; + } + + tarteaucitron.addScript('//app.purechat.com/VisitorWidget/WidgetScript', '', function () { + try { + window.w = new PCWidget({ c: tarteaucitron.user.purechatId, f: true }); + } catch (e) {} + }); + } +}; + +// shareaholic +tarteaucitron.services.shareaholic = { + "key": "shareaholic", + "type": "social", + "name": "Shareaholic", + "uri": "https://shareaholic.com/privacy/choices", + "needConsent": true, + "cookies": ['__utma', '__utmb', '__utmc', '__utmz', '__utmt_Shareaholic%20Pageviews'], + "js": function () { + "use strict"; + if (tarteaucitron.user.shareaholicSiteId === undefined) { + return; + } + + tarteaucitron.fallback(['shareaholic-canvas'], ''); + tarteaucitron.addScript('//dsms0mj1bbhn4.cloudfront.net/assets/pub/shareaholic.js', '', function () { + try { + Shareaholic.init(tarteaucitron.user.shareaholicSiteId); + } catch (e) {} + }); + }, + "fallback": function () { + "use strict"; + var id = 'shareaholic'; + tarteaucitron.fallback(['shareaholic-canvas'], tarteaucitron.engage(id)); + } +}; + +// shareasale +tarteaucitron.services.shareasale = { + "key": "shareasale", + "type": "ads", + "name": "ShareASale", + "uri": "https://www.shareasale.com/PrivacyPolicy.pdf", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + var uniqIds = [], + i, + uri; + + tarteaucitron.fallback(['shareasale-canvas'], function (x) { + var uniqId = '_' + Math.random().toString(36).substr(2, 9); + uniqIds.push(uniqId); + return '
'; + }); + + for (i = 0; i < uniqIds.length; i += 1) { + uri = 'https://shareasale.com/sale.cfm?'; + uri += 'amount=' + document.getElementById(uniqIds[i]).getAttribute('amount') + '&'; + uri += 'tracking=' + document.getElementById(uniqIds[i]).getAttribute('tracking') + '&'; + uri += 'transtype=' + document.getElementById(uniqIds[i]).getAttribute('transtype') + '&'; + uri += 'persale=' + document.getElementById(uniqIds[i]).getAttribute('persale') + '&'; + uri += 'perlead=' + document.getElementById(uniqIds[i]).getAttribute('perlead') + '&'; + uri += 'perhit=' + document.getElementById(uniqIds[i]).getAttribute('perhit') + '&'; + uri += 'merchantID=' + document.getElementById(uniqIds[i]).getAttribute('merchantID'); + + document.getElementById(uniqIds[i]).innerHTML = ''; + } + }, + "fallback": function () { + "use strict"; + var id = 'shareasale'; + tarteaucitron.fallback(['shareasale-canvas'], tarteaucitron.engage(id)); + } +}; + +// sharethis +tarteaucitron.services.sharethis = { + "key": "sharethis", + "type": "social", + "name": "ShareThis", + "uri": "http://www.sharethis.com/legal/privacy/", + "needConsent": true, + "cookies": ['__unam'], + "js": function () { + "use strict"; + if (tarteaucitron.user.sharethisPublisher === undefined) { + return; + } + var switchTo5x = true, + uri = ('https:' === document.location.protocol ? 'https://ws' : 'http://w') + '.sharethis.com/button/buttons.js'; + + tarteaucitron.fallback(['tacSharethis'], ''); + tarteaucitron.addScript(uri, '', function () { + stLight.options({publisher: tarteaucitron.user.sharethisPublisher, doNotHash: false, doNotCopy: false, hashAddressBar: false}); + }); + + if (tarteaucitron.isAjax === true) { + if (typeof stButtons !== "undefined") { + stButtons.locateElements(); + } + } + }, + "fallback": function () { + "use strict"; + var id = 'sharethis'; + tarteaucitron.fallback(['tacSharethis'], tarteaucitron.engage(id)); + } +}; + +// slideshare +tarteaucitron.services.slideshare = { + "key": "slideshare", + "type": "video", + "name": "SlideShare", + "uri": "https://www.linkedin.com/legal/privacy-policy", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + tarteaucitron.fallback(['slideshare-canvas'], function (x) { + var id = x.getAttribute("data-id"), + width = x.getAttribute("width"), + height = x.getAttribute("height"), + url = '//www.slideshare.net/slideshow/embed_code/' + id; + + return ''; + }); + }, + "fallback": function () { + "use strict"; + var id = 'slideshare'; + tarteaucitron.fallback(['slideshare-canvas'], function (elem) { + elem.style.width = elem.getAttribute('width') + 'px'; + elem.style.height = elem.getAttribute('height') + 'px'; + return tarteaucitron.engage(id); + }); + } +}; + +// statcounter +tarteaucitron.services.statcounter = { + "key": "statcounter", + "type": "analytic", + "name": "StatCounter", + "uri": "https://fr.statcounter.com/about/legal/#privacy", + "needConsent": true, + "cookies": ['sc_is_visitor_unique'], + "js": function () { + "use strict"; + var uniqIds = [], + i, + uri = '//statcounter.com/counter/counter.js'; + + tarteaucitron.fallback(['statcounter-canvas'], function (x) { + var uniqId = '_' + Math.random().toString(36).substr(2, 9); + uniqIds.push(uniqId); + return '
'; + }); + + for (i = 0; i < uniqIds.length; i += 1) { + tarteaucitron.makeAsync.init(uri, uniqIds[i]); + } + }, + "fallback": function () { + "use strict"; + var id = 'statcounter'; + tarteaucitron.fallback(['statcounter-canvas'], tarteaucitron.engage(id)); + } +}; + +// timelinejs +tarteaucitron.services.timelinejs = { + "key": "timelinejs", + "type": "api", + "name": "Timeline JS", + "uri": "http://timeline.knightlab.com/#help", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + tarteaucitron.fallback(['timelinejs-canvas'], function (x) { + var spreadsheet_id = x.getAttribute("spreadsheet_id"), + width = x.getAttribute("width"), + height = x.getAttribute("height"), + lang = x.getAttribute("lang_2_letter"), + font = x.getAttribute("font"), + map = x.getAttribute("map"), + start_at_end = x.getAttribute("start_at_end"), + hash_bookmark = x.getAttribute("hash_bookmark"), + start_at_slide = x.getAttribute("start_at_slide"), + start_zoom = x.getAttribute("start_zoom"), + url = '//cdn.knightlab.com/libs/timeline/latest/embed/index.html?source=' + spreadsheet_id + '&font=' + font + '&maptype=' + map + '&lang=' + lang + '&start_at_end=' + start_at_end + '&hash_bookmark=' + hash_bookmark + '&start_at_slide=' + start_at_slide + '&start_zoom_adjust=' + start_zoom + '&height=' + height; + + return ''; + }); + }, + "fallback": function () { + "use strict"; + var id = 'timelinejs'; + tarteaucitron.fallback(['timelinejs-canvas'], function (elem) { + elem.style.width = elem.getAttribute('width') + 'px'; + elem.style.height = elem.getAttribute('height') + 'px'; + return tarteaucitron.engage(id); + }); + } +}; + +// typekit +tarteaucitron.services.typekit = { + "key": "typekit", + "type": "api", + "name": "Typekit (adobe)", + "uri": "http://www.adobe.com/fr/privacy.html", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + if (tarteaucitron.user.typekitId === undefined) { + return; + } + tarteaucitron.addScript('//use.typekit.net/' + tarteaucitron.user.typekitId + '.js', '', function () { + try { + Typekit.load(); + } catch (e) {} + }); + } +}; + +// twenga +tarteaucitron.services.twenga = { + "key": "twenga", + "type": "ads", + "name": "Twenga", + "uri": "http://www.twenga.com/privacy.php", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + + if (tarteaucitron.user.twengaId === undefined || tarteaucitron.user.twengaLocale === undefined) { + return; + } + + tarteaucitron.addScript('//tracker.twenga.' + tarteaucitron.user.twengaLocale + '/st/tracker_' + tarteaucitron.user.twengaId + '.js'); + } +}; + +// twitter +tarteaucitron.services.twitter = { + "key": "twitter", + "type": "social", + "name": "Twitter", + "uri": "https://support.twitter.com/articles/20170514", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + tarteaucitron.fallback(['tacTwitter'], ''); + tarteaucitron.addScript('//platform.twitter.com/widgets.js', 'twitter-wjs'); + }, + "fallback": function () { + "use strict"; + var id = 'twitter'; + tarteaucitron.fallback(['tacTwitter'], tarteaucitron.engage(id)); + } +}; + +// twitter embed +tarteaucitron.services.twitterembed = { + "key": "twitterembed", + "type": "social", + "name": "Twitter (cards)", + "uri": "https://support.twitter.com/articles/20170514", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + var uniqIds = [], + i, + e, + html; + + tarteaucitron.fallback(['twitterembed-canvas'], function (x) { + var uniqId = '_' + Math.random().toString(36).substr(2, 9); + uniqIds.push(uniqId); + html = '
'; + return video_frame; + }); + }, + "fallback": function () { + "use strict"; + var id = 'vimeo'; + tarteaucitron.fallback(['vimeo_player'], function (elem) { + elem.style.width = elem.getAttribute('width') + 'px'; + elem.style.height = elem.getAttribute('height') + 'px'; + return tarteaucitron.engage(id); + }); + } +}; + +// visualrevenue +tarteaucitron.services.visualrevenue = { + "key": "visualrevenue", + "type": "analytic", + "name": "VisualRevenue", + "uri": "http://www.outbrain.com/legal/privacy-713/", + "needConsent": true, + "cookies": ['__vrf', '__vrm', '__vrl', '__vry', '__vru', '__vrid', '__vrz'], + "js": function () { + "use strict"; + if (tarteaucitron.user.visualrevenueId === undefined) { + return; + } + window._vrq = window._vrq || []; + window._vrq.push(['id', tarteaucitron.user.visualrevenueId]); + window._vrq.push(['automate', true]); + window._vrq.push(['track', function () {}]); + tarteaucitron.addScript('http://a.visualrevenue.com/vrs.js'); + } +}; + +// vshop +tarteaucitron.services.vshop = { + "key": "vshop", + "type": "ads", + "name": "vShop", + "uri": "http://vshop.fr/privacy-policy", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + tarteaucitron.fallback(['vcashW'], ''); + tarteaucitron.addScript('//vshop.fr/js/w.js'); + }, + "fallback": function () { + "use strict"; + var id = 'vshop'; + tarteaucitron.fallback(['vcashW'], tarteaucitron.engage(id)); + } +}; + +// wysistat +tarteaucitron.services.wysistat = { + "key": "wysistat", + "type": "analytic", + "name": "Wysistat", + "uri": "http://wysistat.net/contact/", + "needConsent": true, + "cookies": ['Wysistat'], + "js": function () { + "use strict"; + if (tarteaucitron.user.wysistat === undefined) { + return; + } + tarteaucitron.addScript('//www.wysistat.com/statistique.js', '', function () { + window.stat(tarteaucitron.user.wysistat.cli, tarteaucitron.user.wysistat.frm, tarteaucitron.user.wysistat.prm, tarteaucitron.user.wysistat.ce, tarteaucitron.user.wysistat.page, tarteaucitron.user.wysistat.roi, tarteaucitron.user.wysistat.prof, tarteaucitron.user.wysistat.cpt); + }); + } +}; + +// xiti +tarteaucitron.services.xiti = { + "key": "xiti", + "type": "analytic", + "name": "Xiti", + "uri": "http://www.atinternet.com/politique-du-respect-de-la-vie-privee/", + "needConsent": true, + "cookies": [], + "js": function () { + "use strict"; + if (tarteaucitron.user.xitiId === undefined) { + return; + } + var Xt_param = 's=' + tarteaucitron.user.xitiId + '&p=', + Xt_r, + Xt_h, + Xt_i, + Xt_s, + div = document.createElement('div'); + try { + Xt_r = top.document.referrer; + } catch (e) { + Xt_r = document.referrer; + } + Xt_h = new Date(); + Xt_i = '= 4) { + Xt_s = screen; + Xt_i += '&r=' + Xt_s.width + 'x' + Xt_s.height + 'x' + Xt_s.pixelDepth + 'x' + Xt_s.colorDepth; + } + + div.innerHTML = Xt_i + '&ref=' + Xt_r.replace(/[<>"]/g, '').replace(/&/g, '$') + '" title="Internet Audience">'; + document.getElementsByTagName('body')[0].appendChild(div.firstChild); + + if (typeof tarteaucitron.user.xitiMore === 'function') { + tarteaucitron.user.xitiMore(); + } + } +}; + +// youtube +tarteaucitron.services.youtube = { + "key": "youtube", + "type": "video", + "name": "YouTube", + "uri": "https://www.google.fr/intl/fr/policies/privacy/", + "needConsent": true, + "cookies": ['VISITOR_INFO1_LIVE', 'YSC', 'PREF', 'GEUP'], + "js": function () { + "use strict"; + tarteaucitron.fallback(['youtube_player'], function (x) { + var video_id = x.getAttribute("videoID"), + video_width = x.getAttribute("width"), + frame_width = 'width=', + video_height = x.getAttribute("height"), + frame_height = 'height=', + video_frame, + params = 'theme=' + x.getAttribute("theme") + '&rel=' + x.getAttribute("rel") + '&controls=' + x.getAttribute("controls") + '&showinfo=' + x.getAttribute("showinfo") + '&autoplay=' + x.getAttribute("autoplay"); + + if (video_id === undefined) { + return ""; + } + if (video_width !== undefined) { + frame_width += '"' + video_width + '" '; + } else { + frame_width += '"" '; + } + if (video_height !== undefined) { + frame_height += '"' + video_height + '" '; + } else { + frame_height += '"" '; + } + video_frame = ''; + return video_frame; + }); + }, + "fallback": function () { + "use strict"; + var id = 'youtube'; + tarteaucitron.fallback(['youtube_player'], function (elem) { + elem.style.width = elem.getAttribute('width') + 'px'; + elem.style.height = elem.getAttribute('height') + 'px'; + return tarteaucitron.engage(id); + }); + } +}; + +// youtube playlist +tarteaucitron.services.youtubeplaylist = { + "key": "youtubeplaylist", + "type": "video", + "name": "YouTube (playlist)", + "uri": "https://www.google.fr/intl/fr/policies/privacy/", + "needConsent": true, + "cookies": ['VISITOR_INFO1_LIVE', 'YSC', 'PREF', 'GEUP'], + "js": function () { + "use strict"; + tarteaucitron.fallback(['youtube_playlist_player'], function (x) { + var playlist_id = x.getAttribute("playlistID"), + video_width = x.getAttribute("width"), + frame_width = 'width=', + video_height = x.getAttribute("height"), + frame_height = 'height=', + video_frame, + params = 'theme=' + x.getAttribute("theme") + '&rel=' + x.getAttribute("rel") + '&controls=' + x.getAttribute("controls") + '&showinfo=' + x.getAttribute("showinfo") + '&autoplay=' + x.getAttribute("autoplay"); + + if (playlist_id === undefined) { + return ""; + } + if (video_width !== undefined) { + frame_width += '"' + video_width + '" '; + } else { + frame_width += '"" '; + } + if (video_height !== undefined) { + frame_height += '"' + video_height + '" '; + } else { + frame_height += '"" '; + } + video_frame = ''; + return video_frame; + }); + }, + "fallback": function () { + "use strict"; + var id = 'youtubeplaylist'; + tarteaucitron.fallback(['youtube_playlist_player'], function (elem) { + elem.style.width = elem.getAttribute('width') + 'px'; + elem.style.height = elem.getAttribute('height') + 'px'; + return tarteaucitron.engage(id); + }); + } +}; + +// zopim +tarteaucitron.services.zopim = { + "key": "zopim", + "type": "support", + "name": "Zopim", + "uri": "https://www.zopim.com/privacy", + "needConsent": true, + "cookies": ['__zlcid', '__zprivacy'], + "js": function () { + "use strict"; + if (tarteaucitron.user.zopimID === undefined) { + return; + } + tarteaucitron.addScript('//v2.zopim.com/?' + tarteaucitron.user.zopimID); + } +}; + +// xiti smartTag +tarteaucitron.services.xiti_smarttag = { + "key": "xiti_smarttag", + "type": "analytic", + "name": "Xiti (SmartTag)", + "uri": "https://www.atinternet.com/societe/protection-des-donnees/", + "needConsent": true, + "cookies": ["atidvisitor", "atreman", "atredir", "atsession", "atuserid", "attvtreman", "attvtsession"], + "js": function () { + "use strict"; + if (tarteaucitron.user.xiti_smarttagLocalPath !== undefined) { + tarteaucitron.addScript(tarteaucitron.user.xiti_smarttagLocalPath, 'smarttag', null, null, "onload", "addTracker();"); + } else { + var xitiSmarttagId = tarteaucitron.user.xiti_smarttagSiteId; + if (xitiSmarttagId === undefined) { + return; + } + + tarteaucitron.addScript('//tag.aticdn.net/' + xitiSmarttagId + '/smarttag.js', 'smarttag', null, null, "onload", "addTracker();"); + } + } +}; + +// facebook pixel +tarteaucitron.services.facebookpixel = { + "key": "facebookpixel", + "type": "ads", + "name": "Facebook Pixel", + "uri": "https://fr-fr.facebook.com/business/help/www/651294705016616", + "needConsent": true, + "cookies": ['datr', 'fr', 'reg_ext_ref', 'reg_fb_gate', 'reg_fb_ref', 'sb', 'wd', 'x-src'], + "js": function () { + "use strict"; + var n; + if(window.fbq)return; + n=window.fbq=function(){n.callMethod? n.callMethod.apply(n,arguments):n.queue.push(arguments)} ; + if(!window._fbq)window._fbq=n; + n.push=n; + n.loaded=!0; + n.version='2.0'; + n.queue=[]; + tarteaucitron.addScript('https://connect.facebook.net/en_US/fbevents.js'); + fbq('init', tarteaucitron.user.facebookpixelId); + fbq('track', 'PageView'); + + if (typeof tarteaucitron.user.facebookpixelMore === 'function') { + tarteaucitron.user.facebookpixelMore(); + } + } +}; + diff --git a/docs/pages/home.md b/docs/pages/home.md new file mode 100644 index 0000000..23abc2c --- /dev/null +++ b/docs/pages/home.md @@ -0,0 +1,30 @@ +

+ Arta +

+

+ An Open Source Rules Engine - Make rule handling simple +

+

+ Versions +

+ +# Welcome to the documentation + +* Want to discover what is **Arta**? :arrow_right: [Get Started](a_simple_example.md) +* Want to know how to use it? :arrow_right: [User Guide](how_to.md) + +!!! info "New feature" + + Check out the new and very convenient feature called the [simple condition](how_to.md#simple-condition). A new and lightweight way of configuring your rules' conditions. + +**Arta** is automatically tested with: + +![Alt Python](https://img.shields.io/pypi/pyversions/arta) + +!!! tip "Releases" + + Want to see last updates, check the [Release notes](https://github.com/MAIF/arta/releases) :rocket: + +!!! success "Pydantic 2" + + **Arta** is now working with [Pydantic 2](https://docs.pydantic.dev/latest/)! And of course, Pydantic 1 as well. diff --git a/docs/pages/how_to.md b/docs/pages/how_to.md index cb4dca2..4315116 100644 --- a/docs/pages/how_to.md +++ b/docs/pages/how_to.md @@ -1,246 +1,107 @@ Ensure that you have correctly installed **Arta** before, check the [Installation](installation.md) page :wrench: -## Hello World +## Simple condition -You want a simple code to play with? Here it comes: +!!! example "Beta feature" -=== "Without type hints" - - ```python - from arta import RulesEngine - - set_admission = lambda value, **kwargs: {"is_admitted": value} - - rules = { - "check_admission": { - "ADMITTED_RULE": { - "condition": lambda power: power in ["strength", "fly", "immortality"], - "condition_parameters": {"power": "input.super_power"}, - "action": set_admission, - "action_parameters": {"value": True}, - }, - "DEFAULT_RULE": { - "condition": None, - "condition_parameters": None, - "action": set_admission, - "action_parameters": {"value": False}, - }, - } - } - - input_data = { - "id": 1, - "name": "Superman", - "civilian_name": "Clark Kent", - "age": None, - "city": "Metropolis", - "language": "french", - "super_power": "fly", - "favorite_meal": "Spinach", - "secret_weakness": "Kryptonite", - "weapons": [], - } - - eng = RulesEngine(rules_dict=rules) - - result = eng.apply_rules(input_data) - - print(result) - ``` - -=== "With type hints (>=3.9)" - - ```python - from typing import Any, Callable - - from arta import RulesEngine - - set_admission: Callable = lambda value, **kwargs: {"is_admitted": value} - - rules: dict[str, Any] = { - "check_admission": { - "ADMITTED_RULE": { - "condition": lambda power: power in ["strength", "fly", "immortality"], - "condition_parameters": {"power": "input.super_power"}, - "action": set_admission, - "action_parameters": {"value": True}, - }, - "DEFAULT_RULE": { - "condition": None, - "condition_parameters": None, - "action": set_admission, - "action_parameters": {"value": False}, - }, - } - } - - input_data: dict[str, Any] = { - "id": 1, - "name": "Superman", - "civilian_name": "Clark Kent", - "age": None, - "city": "Metropolis", - "language": "french", - "super_power": "fly", - "favorite_meal": "Spinach", - "secret_weakness": "Kryptonite", - "weapons": [], - } - - eng = RulesEngine(rules_dict=rules) + **Simple condition** is still a *beta feature*, some cases could not work as designed. - result: dict[str, Any] = eng.apply_rules(input_data) - - print(result) - ``` +**Simple conditions** are a new and straightforward way of configuring your *conditions*. -You should get: +It simplifies a lot your rules by: - {'check_admission': {'is_admitted': True}} - -!!! success - Superman is admitted to the superhero school! - -Well done! By executing this code you have: - -1. Defined an **action function** (`set_admission`) -2. Defined a **rule set** (`rules`) -3. Used some **input data** (`input_data`) -4. Instanciated a **rules engine** (`RulesEngine`) -5. Applied the rules on the data and get some results (`.apply_rules()`) +* Removing the use of a `conditions.py` module (no validation functions needed). +* Removing the `conditions:` configuration key in your YAML files. !!! note - In the code example we used some anonymous/lambda function for simplicity but it could be regular python functions as well. - -!!! abstract "API Documentation" - - You can get details on the `RulesEngine` parameters in the [API Reference](api_reference.md). - -Have you read the [Get Started](in_a_nutshell.md) section? If not, you probably should before going further :smiley: - -## Concepts - -Let's go deeper into the previous code: - -### Rule sets and rule groups - -A **rule set** is composed of **rule groups** which are themselves composed of **rules**. We can find this tree structure in the following dictionary: - -```python -rules = { # (1) - "check_admission": { # (2) - "ADMITTED_RULE": { # (3) - "condition": lambda power: power in ["strength", "fly", "immortality"], - "condition_parameters": {"power": "input.super_power"}, - "action": set_admission, - "action_parameters": {"value": True}, - }, - "DEFAULT_RULE": { - "condition": None, - "condition_parameters": None, - "action": set_admission, - "action_parameters": {"value": False}, - }, - } -} -``` - -1. This dictionary contains a *rule set*. -2. This key define a *rule group*, we can have many groups (we have only one here for simplicity). -3. This key is a *rule id*, which identifies *rules* among others. - -### Rules - -**Rules** are identified by an id or key (e.g., `ADMITTED_RULE`) and defined by a dictionary: - -```python hl_lines="2-5" -"ADMITTED_RULE": { - "condition": lambda power: power in ["strength", "fly", "immortality"], - "condition_parameters": {"power": "input.super_power"}, - "action": set_admission, - "action_parameters": {"value": True}, -} -``` - -!!! tip + With the **simple conditions** you use straight *boolean expressions* directly in your configuration. + + It is easyer to read and maintain :+1: - Rule **ids** are in capital letters for readability only: it is an advised best practice. +The *configuration key* here is: -**Rules** are made of 2 different things: +> `simple_condition:` -* Condition: +Example : -```python hl_lines="2 3" -{ - "condition": lambda power: power in ["strength", "fly", "immortality"], - "condition_parameters": {"power": "input.super_power"}, - "action": set_admission, - "action_parameters": {"value": True}, -} -``` - -* Action: +```yaml hl_lines="6 11 16" +--- +rules: + default_rule_set: + admission: + ADM_OK: + simple_condition: input.power=="strength" or input.power=="fly" + action: set_admission + action_parameters: + value: OK + ADM_TO_BE_CHECKED: + simple_condition: input.age>=150 and input.age!=None + action: set_admission + action_parameters: + value: TO_CHECK + ADM_KO: + simple_condition: null + action: set_admission + action_parameters: + value: KO -```python hl_lines="4 5" -{ - "condition": lambda power: power in ["strength", "fly", "immortality"], - "condition_parameters": {"power": "input.super_power"}, - "action": set_admission, - "action_parameters": {"value": True}, -} +actions_source_modules: + - my_folder.actions # (1) ``` -### Conditions and Actions +1. Contains *action function* implementations, no need of the key `conditions_source_modules` here. -**Conditions** and **actions** are quite similar in terms of implementation but their goal is different. +How to write a simple condition like: -Both are made of a *callable object* and *parameters*: + input.power=="strength" or input.power=="fly" -* Condition keys: - * `condition`: a callable python object that returns a `bool`, we called this function the **validation function** (or *condition function*). - * `condition_parameters`: a dictionary mapping the validation function's parameters with their correponding values. -* Action keys: - * `action`: a callable python object that returns what you want (or does what you want such as: requesting an api, sending an email, etc.), we called this function the **action function**. - * `action_parameters`: a dictionary mapping the action function's parameters with their correponding values. +* **Left operand (data mapping):** + * You must use one of the following prefixes: + * `input` (for input data) + * `output` (for previous rule's result) + * A *dot path* expression like `input.powers.main_power`. +* **Operator:** you must use basic python *boolean operator* (i.e., `==, <, >, <=, >=, !=`) +* **Right operand:** basic python data types (e.i., `str, int, None`). -!!! question "Does a condition could be something else than a function?" +!!! warning - Actually yes, a `condition` can be a python function but you will learn later that it can also be a **condition expression** (i.e., a boolean expression combining different individual conditions). + * You can't use: `is` or `in`, as an **operator** (yet). + * You can't use a `float` as **right operand** (it's a bug, will be fixed). + * For strings, don't forget the **double quotes** `"`. -!!! tip "Parameter's special syntax" +!!! danger "Security concern" - As you can see in the previous code, the action and condition parameters can have a special syntax: + **Python code injection:** - {"power": "input.super_power"} + Because **Arta** is using the `eval()` built-in function to evaluate *simple conditions*: - The string `input.super_power` is evaluated by the rules engine and it means *"fetch the key `super_power` in the input data"*. Keep reading, you will find out later. - -## Configuration + * **You should never let the user** being able of dynamically define a *simple condition* (in `simple_condition:` conf. key). + * You should verify that **write permissions on the YAML files** are not allowed when your app is deployed. -In the [Hello World](#hello-world) section of this user guide, you learnt how to instanciate and use the *rules engine* with a dictionary **rule set**. It's the reason why you used the correponding parameter `rules_dict` for the instancation: +## Standard condition - eng = RulesEngine(rules_dict=rules) +It is the first implemented way of using **Arta** and probably the most powerful. -But there is another way to define your rules: using a **configuration** (i.e., some configuration files). +The *configuration key* here is: -For real use cases, using configuration is way much more convenient than using a dictionary :+1: +> `condition:` !!! info "YAML" - The built-in file format used by Arta for configuration is YAML. + The built-in file format used by Arta for configuration is **YAML**. !!! example "Enhancement proposal" - We are thinking on a design that will allow custom configuration backend which will allow user-implemented loading of the configuration (e.g., you prefer using a JSON format). Stay tuned. + We are thinking on a design that will allow user-implemented loading of the configuration (e.g., if you prefer using a JSON format). Stay tuned. ### YAML file !!! tip "Simple Conditions" - The following **YAML** example illustrates how to configure usual *standard conditions* but there is another and simpler way to do it by using a special feature: the [simple condition](special_conditions.md#simple-condition). + The following **YAML** example illustrates how to configure usual *standard conditions* but there is another and simpler way to do it by using a special feature: the [simple condition](#simple-condition). -Create a YAML file and define your rules almost like you did with the dictionary `rules`. There is few differences that we will focus on later: +Create a YAML file and define your rules like this: ```yaml hl_lines="6 16-26" --- @@ -286,8 +147,8 @@ actions_source_modules: # (5) You can split your configuration in multiple YAML files seamlessly in order to keep things clear. Example: * global.yaml => source modules - * rules.yaml => rule definitions - * conditions.yaml => condition definitions + * rules.yaml => rules' definition + * conditions.yaml => conditions' definition It's very convenient when you have a lot of different rules and conditions in your app. @@ -307,7 +168,7 @@ rules: value: true ``` -As it was previously mentionned, the key `condition:` can take one **condition id** but also a **condition expression** (i.e., a boolean expression of condition ids) combining several conditions: +The key `condition:` can take one **condition id** but also a **condition expression** (i.e., a boolean expression of condition ids) combining several conditions: ```yaml hl_lines="6" --- @@ -323,7 +184,7 @@ rules: !!! warning - In that example, you must define the 3 condition ids in the configuration: + In that example, you must define the 3 conditions in the configuration: * HAS_SCHOOL_AUTHORIZED_POWER * SPEAKS_FRENCH @@ -333,7 +194,7 @@ rules: Use the **condition expressions** to keep things simple. Put your conditions in one expression as you can rather than creating several rules :wink: -### Implementing functions +### Functions We must create 2 modules: @@ -393,22 +254,235 @@ result = eng.apply_rules(input_data) print(result) ``` -You should get the same result as [previously](#hello-world) (dictionary version): +You should get: - {'check_admission': {'is_admitted': True}} +> `{'check_admission': {'is_admitted': True}}` + +!!! abstract "API Documentation" + + You can get details on the `RulesEngine` parameters in the [API Reference](api_reference.md). + +## Concepts + +Let's go deeper into the concepts. + +### Rule set and rule group + +A **rule set** is composed of **rule groups** which are themselves composed of **rules**. We can find this tree structure in the following YAML: + +```yaml +--- +rules: + default_rule_set: # (1) + check_admission: # (2) + ADMITTED_RULE: # (3) + condition: HAS_SCHOOL_AUTHORIZED_POWER + action: set_admission + action_parameters: + value: true + DEFAULT_RULE: + condition: null + action: set_admission + action_parameters: + value: false -## To sum up +conditions: + HAS_SCHOOL_AUTHORIZED_POWER: + description: "Does applicant have a school authorized power?" + validation_function: has_authorized_super_power + condition_parameters: + power: input.super_power +``` -At this point you have learnt the regular use of an **Arta** rules engine and you have seen the two major ways of defining rules: +1. This is the id of the *rule set*. +2. This key define a *rule group*, we can have many groups (we have only one here for simplicity). +3. This key is a *rule id*, which identifies *rules* among others. + +### Rule + +**Rule** definitions are identified by an id (e.g., `ADMITTED_RULE`): -* [Using a dictionary of rules](#hello-world). -* [Using a configuration file](#configuration) (or many). +```yaml hl_lines="2-5" + ADMITTED_RULE: + condition: HAS_SCHOOL_AUTHORIZED_POWER + action: set_admission + action_parameters: + value: true +``` !!! tip + Rule **ids** are in capital letters for readability only: it is an advised practice. + +**Rules** are made of 2 different things: + +* Condition: + +```yaml hl_lines="2" + ADMITTED_RULE: + condition: HAS_SCHOOL_AUTHORIZED_POWER + action: set_admission + action_parameters: + value: true +``` + +* Action: + +```yaml hl_lines="3-5" + ADMITTED_RULE: + condition: HAS_SCHOOL_AUTHORIZED_POWER + action: set_admission + action_parameters: + value: true +``` + +### Condition and Action + +**Conditions** and **actions** are quite similar in terms of implementation but their goals are different. + +Both are made of a *callable object* and some *parameters*: + +* **Condition keys:** + + * *`validation_function`: name of a callable python object that returns a `bool`, we called this function the *validation function* (or *condition function*). + + * `condition_parameters`: the validation function's arguments. + +* **Action keys:** + + * `action`: name of a callable python object that returns what you want (or does what you want such as: requesting an api, sending an email, etc.), we called this function the *action function*. + + * `action_parameters`: the action function's arguments. + +!!! tip "Parameter's special syntax" + + The action and condition **arguments** can have a special syntax: + + condition_parameters: + power: input.super_power + + The string `input.super_power` is evaluated by the rules engine and it means *"fetch the key `super_power` in the input data"*. + +## Dictionary rules + +**Rules** can be configured in a YAML file but they can also be defined by a regular dictionary: + +=== "Without type hints" + + ```python + from arta import RulesEngine + + set_admission = lambda value, **kwargs: {"is_admitted": value} + + rules = { + "check_admission": { + "ADMITTED_RULE": { + "condition": lambda power: power in ["strength", "fly", "immortality"], + "condition_parameters": {"power": "input.super_power"}, + "action": set_admission, + "action_parameters": {"value": True}, + }, + "DEFAULT_RULE": { + "condition": None, + "condition_parameters": None, + "action": set_admission, + "action_parameters": {"value": False}, + }, + } + } + + input_data = { + "id": 1, + "name": "Superman", + "civilian_name": "Clark Kent", + "age": None, + "city": "Metropolis", + "language": "french", + "super_power": "fly", + "favorite_meal": "Spinach", + "secret_weakness": "Kryptonite", + "weapons": [], + } + + eng = RulesEngine(rules_dict=rules) + + result = eng.apply_rules(input_data) + + print(result) + ``` + +=== "With type hints (>=3.9)" + + ```python + from typing import Any, Callable + + from arta import RulesEngine + + set_admission: Callable = lambda value, **kwargs: {"is_admitted": value} + + rules: dict[str, Any] = { + "check_admission": { + "ADMITTED_RULE": { + "condition": lambda power: power in ["strength", "fly", "immortality"], + "condition_parameters": {"power": "input.super_power"}, + "action": set_admission, + "action_parameters": {"value": True}, + }, + "DEFAULT_RULE": { + "condition": None, + "condition_parameters": None, + "action": set_admission, + "action_parameters": {"value": False}, + }, + } + } + + input_data: dict[str, Any] = { + "id": 1, + "name": "Superman", + "civilian_name": "Clark Kent", + "age": None, + "city": "Metropolis", + "language": "french", + "super_power": "fly", + "favorite_meal": "Spinach", + "secret_weakness": "Kryptonite", + "weapons": [], + } + + eng = RulesEngine(rules_dict=rules) + + result: dict[str, Any] = eng.apply_rules(input_data) + + print(result) + ``` + +You should get: + + {'check_admission': {'is_admitted': True}} + +!!! success + Superman is admitted to the superhero school! + +Well done! By executing this code you have: + +1. Defined an **action function** (`set_admission`) +2. Defined a **rule set** (`rules`) +3. Used some **input data** (`input_data`) +4. Instanciated a **rules engine** (`RulesEngine`) +5. Applied the rules on the data and get some results (`.apply_rules()`) + +!!! note + + In the code example we used some anonymous/lambda function for simplicity but it could be regular python functions as well. + +!!! tip "YAML vs Dictionary" + **How to choose between dictionary and configuration?** - In most cases, you must choose the configuration way of defining your rules. You will improve your rules' maintainability a lot. - In some cases like proof-of-concepts or Jupyter notebook works, you will probably be happy to use straightforward dictionaries. + *In most cases, you must choose the configuration way of defining your rules.* + + You will improve your rules' maintainability a lot. + In some cases like proof-of-concepts or Jupyter notebook works, you will probably be happy with straightforward dictionaries. **Arta** has plenty more features to discover. If you want to learn more, go to the next chapter: [Advanced User Guide](parameters.md). \ No newline at end of file diff --git a/docs/pages/in_a_nutshell.md b/docs/pages/in_a_nutshell.md deleted file mode 100644 index 354379a..0000000 --- a/docs/pages/in_a_nutshell.md +++ /dev/null @@ -1,199 +0,0 @@ -## Intro - -As we already mentioned in the [Home](index.md) page: ***Arta** is a very simple python rules engine*, but what do we mean by *rules engine*? - -* **rule** : a set of different conditions that can be `True` or `False` (i.e., we say *verified* or *not verified*) triggering an action (i.e., any python callable object). -* **engine** : some code used for combining and evaluating different rules on some input data. - -## Quick example: The Superhero School :school: :superhero: - -Imagine the following use case: - -*Your are managing a superhero school and you want to use some school rules in your python app.* - -The rules are (intentionally simple): - -!!! success "Admission rules" - - If the applicant has a school authorized power then he is admitted, - - Else he is not. - -!!! example "Course selection rules" - - If he is speaking french and his age is known then he must take the "french" course, - - Else if his age is unknown (e.g., it's a very old superhero), then he must take the "senior" course, - - Else if he is not speaking french, then he must take the "international" course. - -!!! info "Send favorite meal rules" - - If he is admitted and has a prefered dish, then we send an email to the school cook with the dish name. - -## Focus on a rule - -If we focus on one rule: - -> If the applicant has a school authorized power then he is admitted, else he is not. - -Here we can identify: - -* The condition: **has a school authorized power** (only one condition) -* The triggered action: **is admitted** - -## Focus on a condition - -Let's be more precise on the following condition: - -> has a school authorized power - -If we define a list of "school authorized powers" it will be easy to verify this condition for an applicant: - -```python -authorized_powers = [ - "strength", - "fly", - "immortality", -] -``` - -## Focus on an action - -Defining the action is probably the hardest part because it means defining the rules engine output, which depends on the use of the rules' results. - -Let's focus on the following *action*: - -> he is admitted - -Let's say that in our use case, a simple dictionary *key* is used for storing the admission status: `{"is_admitted": True}` - -We previously mentioned that action are *python callable object*, so it could be a simple function as: - -```python -def set_admission(value: bool, **kwargs: Any) -> dict[str, bool]: - """Return a dictionary containing the admission result.""" - return {"is_admitted": value} -``` - -We will see later how the `value` argument is passed to this **action function**. - -## Focus on the input data - -The *rules engine* is responsible for evaluating the [configured rules](#quick-example-the-superhero-school) against some *data* (usually named *input data*). - -In our use case, the input data could be a list of applicants: - -```python -applicants = [ - { - "id": 1, - "name": "Superman", - "civilian_name": "Clark Kent", - "age": None, - "city": "Metropolis", - "language": "french", - "powers": ["strength", "fly"], - "favorite_meal": "Spinach", - "secret_weakness": "Kryptonite", - "weapons": [], - }, - { - "id": 2, - "name": "Batman", - "civilian_name": "Bruce Wayne", - "age": 33, - "city": "Gotham City", - "language": "english", - "powers": ["bank_account", "determination", "strength"], - "favorite_meal": None, - "secret_weakness": "Feel alone", - "weapons": ["Hands", "Batarang", "Batgrenade"], - }, - { - "id": 3, - "name": "Wonder Woman", - "civilian_name": "Diana Prince", - "age": 5000, - "city": "Island of Themyscira", - "language": "french", - "powers": ["strength", "greek_gods", "regeneration", "immortality"], - "favorite_meal": None, - "secret_weakness": "Lost faith in humanity", - "weapons": ["Magic lasso", "Bulletproof bracelets", "Sword", "Shield"], - }, -] -``` - -## Focus on the results - -We talked about *rules*, *conditions*, *actions* and then *input data*, it is the **rules engine** responsability to put all them together and output some results. - -To do that we need only two things: - -1. Instanciating a *rules engine* (by giving it the rules' definition). -2. Applying the rules on the *input data*. - -The first task is explained in the [User Guide](how_to.md) section as the second but if you are curious you will find a simple example below of how to apply the rules on a data set. - -Let's apply the rules on a single applicant of our data set: - -```python -from arta import RulesEngine - -eng = RulesEngine(config_path="/to/my/config/dir") # (1) - -result = eng.apply_rules(applicants[0]) - -print(result) # (2) -# { -# "admission": {"is_admitted": True}, -# "course_selection": {"course": "senior"}, -# "send_dish": True -# } -``` - -1. Many possibilites for instanciation, we will explain them later -2. Print a single result for the first applicant - -In the *rules engine* result, we have 3 outputs: - -* `"admission": {"is_admitted": True},` -* `"course_selection": {"course": "senior"},` -* `"send_dish": True` - -It's simple, each correpond to one [rule](#quick-example-the-superhero-school). - -Then we can apply the rules to all the data set (only 3 applicants): - -```python -from arta import RulesEngine - -results = {applicant["name"]: eng.apply_rules(applicant) for applicant in applicants} - -print(results) # (1) -# { -# "Superman": { -# "admission": {"is_admitted": True}, -# "course_selection": {"course": "senior"}, -# "send_dish": True}, -# "Batman": { -# "admission": {"is_admitted": True}, -# "course_selection": {"course": "international"}, -# "send_dish": False, -# }, -# "Wonder Woman": { -# "admission": {"is_admitted": True}, -# "course_selection": {"course": "french"}, -# "send_dish": False, -# } -# } -``` - -1. Print the results of all applicants - -!!! success "Human readable format of the result" - - Superman, Batman and Wonder Womam are all admitted to school. Superman to the "senior" course, Batman to the "international" course and Wonder Woman to the "french" one. An email has been sent to the cook with Superman's favorite meal 'spinach'. - -Now, if you want to learn how to configure your rules, go to the [User Guide](how_to.md) section. \ No newline at end of file diff --git a/docs/pages/index.html b/docs/pages/index.html new file mode 100644 index 0000000..f9a774a --- /dev/null +++ b/docs/pages/index.html @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + Arta by MAIF + + + + + +
+ +
+
+
+ +
+
+
+
    +
  • An Open Source
  • +
  • Rules Engine:
  • +
  • Make rule handling simple
  • +
+

+ It's a simple python rules engine designed for python developers.

+ There is one main reason for using Arta and it was the main goal of its development + at MAIF: increase business rules maintainability. +
+
+ In other words, facilitate rules handling in a python app. +

+
+

Features

+
    +
  • Standardize the definition of a rule. All rules are defined the same way + in a unique place.

  • +
  • Rules are released from the code base, which is less error prone and + increases clearness.

  • +
  • Use Arta whatever your field is.

  • +
  • Great combination with Machine Learning: groups all the + deterministic rules of your ML projects.
  • +
+
+
+
+ +

easy to use

+

Configure your rules in YAML files

+ +

customizable

+

Organize your rules like you want, create new conditions

+ +

readability

+

Easy to read, easy to maintain

+ +

Machine Learning

+

Things are in the right place

+

(deterministic VS probabilistic)

+ +
+
+
+
+
+
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/docs/pages/index.md b/docs/pages/index.md deleted file mode 100644 index 18271ca..0000000 --- a/docs/pages/index.md +++ /dev/null @@ -1,26 +0,0 @@ -# Welcome to the Arta documentation - -**Arta** is a very simple python *rules engine* designed for and by python developers. - -* Want to discover what is **Arta**? :arrow_right: [Get Started](in_a_nutshell.md) -* Want to know how to use it? :arrow_right: [User Guide](how_to.md) - -!!! info "New feature" - - Check out the new and very convenient feature called the [simple condition](special_conditions.md#simple-condition). A new and lightweight way of configuring your rules' conditions. - -**Arta** is automatically tested with: - -![Python](https://img.shields.io/badge/Python-3.9_--_3.12-blue) - -!!! example "Hello World" - - Want to try it very quickly on a very simple use case? Go to the [Hello World](how_to.md#hello-world) section. - -!!! tip "Releases" - - Want to see last updates, check the [Release notes](https://github.com/MAIF/arta/releases) :rocket: - -!!! success "Pydantic 2" - - **Arta** is now working with [Pydantic 2](https://docs.pydantic.dev/latest/)! And of course, Pydantic 1 as well. \ No newline at end of file diff --git a/docs/pages/installation.md b/docs/pages/installation.md index 00707fe..b29fc06 100644 --- a/docs/pages/installation.md +++ b/docs/pages/installation.md @@ -2,7 +2,7 @@ Compatible with: -![Python](https://img.shields.io/badge/Python-3.9_--_3.12-blue) +![Alt Versions](https://img.shields.io/pypi/v/arta) ## pip diff --git a/docs/pages/parameters.md b/docs/pages/parameters.md index aaf1a08..68fa7ae 100644 --- a/docs/pages/parameters.md +++ b/docs/pages/parameters.md @@ -22,7 +22,7 @@ They both can be used in **condition and action parameters**. By default, errors during *condition* and *action parameters* parsing are **raised**. -If we refer to the [Hello World](how_to.md#hello-world) example: +If we refer to the [dictionary](how_to.md#dictionary-rules) example: ```python hl_lines="5" rules = { diff --git a/docs/pages/rule_sets.md b/docs/pages/rule_sets.md index e9d3dc9..e965686 100644 --- a/docs/pages/rule_sets.md +++ b/docs/pages/rule_sets.md @@ -14,7 +14,7 @@ The good news is that different **rule sets** can be used seamlessly with the sa Let's take the following example: -> Based on the [Hello World](how_to.md#hello-world) example, imagine that you need to add some rules about something totally different than the superhero school. Let's say rules for a dinosaur school. +> Based on that [example](how_to.md#standard-condition), imagine that you need to add some rules about something totally different than the superhero school. Let's say rules for a dinosaur school. ## Configuration @@ -77,7 +77,7 @@ actions_source_modules: Now that your **rule sets** are defined (and assuming that your condition and action functions are implemented in the right modules), you can easily use them: -```python +```python hl_lines="29 31" from arta import RulesEngine input_data_1 = { diff --git a/docs/pages/special_conditions.md b/docs/pages/special_conditions.md index 77b180b..5465a5a 100644 --- a/docs/pages/special_conditions.md +++ b/docs/pages/special_conditions.md @@ -1,79 +1,3 @@ -## Simple condition - -!!! example "Beta feature" - - **Simple condition** is still a *beta feature*, some cases could not work as designed. - -**Simple conditions** are a new and straightforward way of configuring your *conditions*. - -It simplifies a lot your rules by: - -* Removing the use of a `conditions.py` module (no validation functions needed). -* Removing the `conditions:` configuration key in your YAML files. - -!!! note - - With the **simple conditions** you use straight *boolean expressions* directly in your configuration. - - It is easyer to read and maintain :+1: - -Example : - -```yaml hl_lines="6 11 16" ---- -rules: - default_rule_set: - admission: - ADM_OK: - simple_condition: input.power=="strength" or input.power=="fly" - action: set_admission - action_parameters: - value: OK - ADM_TO_BE_CHECKED: - simple_condition: input.age>=150 and input.age!=None - action: set_admission - action_parameters: - value: TO_CHECK - ADM_KO: - simple_condition: null - action: set_admission - action_parameters: - value: KO - -actions_source_modules: - - my_folder.actions # (1) -``` - -1. Contains *action function* implementations, no need of the key `conditions_source_modules` here. - -How to write a simple condition like: - - input.power=="strength" or input.power=="fly" - -* **Left operand (data mapping):** - * You must use one of the following prefixes: - * `input` (for input data) - * `output` (for previous rule's result) - * A *dot path* expression like `input.powers.main_power`. -* **Operator:** you must use basic python *boolean operator* (i.e., `==, <, >, <=, >=, !=`) -* **Right operand:** basic python data types (e.i., `str, int, None`). - -!!! warning - - * You can't use: `is` or `in`, as an **operator** (yet). - * You can't use a `float` as **right operand** (it's a bug, will be fixed). - * For strings, don't forget the **double quotes** `"`. - -!!! danger "Security concern" - - **Python code injection:** - - Because **Arta** is using the `eval()` built-in function to evaluate *simple conditions*: - - * **You should never let the user** being able of dynamically define a *simple condition* (in `simple_condition:` conf. key). - * You should verify that **write permissions on the YAML files** are not allowed when your app is deployed. - - ## Custom condition **Custom conditions** are user-defined conditions. @@ -119,47 +43,25 @@ With the **custom conditions** it's quite simple to implement. First, create a class inheriting from `BaseCondtion` and implement the `verify()` method as you want/need: -=== "Python >= 3.10" - - ```python - from typing import Any - - from arta.condition import BaseCondition - from arta.utils import ParsingErrorStrategy - - - class MyCondition(BaseCondition): - def verify( - self, - input_data: dict[str, Any], - parsing_error_strategy: ParsingErrorStrategy, - ) -> bool: - - field, value = tuple(self.condition_id.split("_")) - - return input_data[field.lower()] == value.lower() - ``` - -=== "Python < 3.10" - - ```python - from typing import Any, Optional +```python +from typing import Any - from arta.condition import BaseCondition - from arta.utils import ParsingErrorStrategy +from arta.condition import BaseCondition +from arta.utils import ParsingErrorStrategy - class MyCondition(BaseCondition): - def verify( - self, - input_data: dict[str, Any], - parsing_error_strategy: ParsingErrorStrategy, - ) -> bool: +class MyCondition(BaseCondition): + def verify( + self, + input_data: dict[str, Any], + parsing_error_strategy: ParsingErrorStrategy, + **kwargs: Any + ) -> bool: - field, value = tuple(self.condition_id.split("_")) + field, value = tuple(self.condition_id.split("_")) - return input_data[field.lower()] == value.lower() - ``` + return input_data[field.lower()] == value.lower() +``` !!! example "self.condition_id" @@ -224,8 +126,9 @@ It is based on the following *strategy pattern*: classDiagram note for MyCondition "This is a custom condition class" RulesEngine "1" -- "1..*" Rule - Rule "0..*" -- "0..*" BaseCondition + Rule "1..*" -- "0..*" BaseCondition BaseCondition <|-- StandardCondition + BaseCondition <|-- SimpleCondition BaseCondition <|-- MyCondition class RulesEngine{ +rules diff --git a/docs/pages/why.md b/docs/pages/why.md index 28c0583..46a1e4d 100644 --- a/docs/pages/why.md +++ b/docs/pages/why.md @@ -7,14 +7,14 @@ In other words, facilitate rules handling in a python app. ## Before Arta :spaghetti: -Rules in code can rapidly become a headache, kind of spaghetti dish of `if`, `elif` and `else` (or even `match/case` since Python 3.10) +Rules in code can rapidly become a headache, kind of spaghetti dish of `if`, `elif` and `else` (or even `match/case` since Python 3.10). ## After Arta :sparkles: **Arta** increases rules maintainability: -* By standardizing the definition of a rule. All rules are configured or defined the same way in a unique place (or few). -* Rules are released from the code base, which is less error prone and increases clearness. +* By *standardizing* the definition of a rule. All rules are configured or defined the same way in a unique place (or few). +* Rules are released from the code base, which is *less error prone* and increases clearness. !!! example "Improve collaboration" diff --git a/pyproject.toml b/pyproject.toml index fb16593..6200ee8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,14 +4,14 @@ build-backend = "setuptools.build_meta" [project] name = "arta" -version = "0.7.0" +version = "0.7.0b" requires-python = ">3.8.0" -description = "Arta is a very simple python rules engine designed for and by python developers" +description = "An Open Source Rules Engine - Make rule handling simple" readme = "README.md" authors = [ {name = "develop-cs"}, {name = "HugoPerrier"}, - {name = "Mathis NICOLI"}, + {name = "MathisNcl"}, ] classifiers = [ diff --git a/src/arta/_engine.py b/src/arta/_engine.py index 5f8fb22..2a878bb 100644 --- a/src/arta/_engine.py +++ b/src/arta/_engine.py @@ -3,12 +3,14 @@ Class: RulesEngine """ +from __future__ import annotations + import copy import importlib import inspect from inspect import getmembers, isclass, isfunction from types import FunctionType, MethodType, ModuleType -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type +from typing import Any, Callable from arta.condition import BaseCondition, SimpleCondition, StandardCondition from arta.config import load_config @@ -41,7 +43,7 @@ class RulesEngine: CONST_USER_CONDITION_STRING: str = "USER_CONDITION" # Built-in factory mapping - BUILTIN_FACTORY_MAPPING: Dict[str, Type[BaseCondition]] = { + BUILTIN_FACTORY_MAPPING: dict[str, type[BaseCondition]] = { "condition": StandardCondition, "simple_condition": SimpleCondition, } @@ -49,8 +51,8 @@ class RulesEngine: def __init__( self, *, - rules_dict: Optional[Dict[str, Dict[str, Any]]] = None, - config_path: Optional[str] = None, + rules_dict: dict[str, dict[str, Any]] | None = None, + config_path: str | None = None, ) -> None: """Initialize the rules. @@ -65,8 +67,8 @@ def __init__( TypeError: Wrong type. """ # Var init. - factory_mapping_classes: Dict[str, Type[BaseCondition]] = {} - std_condition_instances: Dict[str, StandardCondition] = {} + factory_mapping_classes: dict[str, type[BaseCondition]] = {} + std_condition_instances: dict[str, StandardCondition] = {} if config_path is not None and rules_dict is not None: raise ValueError("RulesEngine takes only one parameter: 'rules_dict' or 'config_path', not both.") @@ -86,12 +88,12 @@ def __init__( raise KeyError("'rules_dict' couldn't be empty.") # Attribute definition - self.rules: Dict[str, Dict[str, List[Rule]]] = self._adapt_user_rules_dict(rules_dict) + self.rules: dict[str, dict[str, list[Rule]]] = self._adapt_user_rules_dict(rules_dict) # Initialize with a config_path elif config_path is not None: # Load config in attribute - config_dict: Dict[str, Any] = load_config(config_path) + config_dict: dict[str, Any] = load_config(config_path) # Data validation config: Configuration = Configuration(**config_dict) @@ -100,15 +102,15 @@ def __init__( # Set parsing error handling strategy from config self._parsing_error_strategy = ParsingErrorStrategy(config.parsing_error_strategy) - # Dict of available action functions (k: function name, v: function object) - action_modules: List[str] = config.actions_source_modules - action_functions: Dict[str, Callable] = self._get_object_from_source_modules(action_modules) + # dict of available action functions (k: function name, v: function object) + action_modules: list[str] = config.actions_source_modules + action_functions: dict[str, Callable] = self._get_object_from_source_modules(action_modules) - # Dict of available standard condition functions (k: function name, v: function object) - condition_modules: List[str] = ( + # dict of available standard condition functions (k: function name, v: function object) + condition_modules: list[str] = ( config.conditions_source_modules if config.conditions_source_modules is not None else [] ) - std_condition_functions: Dict[str, Callable] = self._get_object_from_source_modules(condition_modules) + std_condition_functions: dict[str, Callable] = self._get_object_from_source_modules(condition_modules) # Dictionary of condition instances (k: condition id, v: instance), built from config data if len(std_condition_functions) > 0: @@ -118,8 +120,8 @@ def __init__( # User-defined/custom conditions if config.condition_factory_mapping is not None and config.custom_classes_source_modules is not None: - # Dict of custom condition classes (k: classe name, v: class object) - custom_condition_classes: Dict[str, Type[BaseCondition]] = self._get_object_from_source_modules( + # dict of custom condition classes (k: classe name, v: class object) + custom_condition_classes: dict[str, type[BaseCondition]] = self._get_object_from_source_modules( config.custom_classes_source_modules ) @@ -145,8 +147,8 @@ def __init__( raise ValueError("RulesEngine needs a parameter: 'rule_dict' or 'config_path'.") def apply_rules( - self, input_data: Dict[str, Any], *, rule_set: Optional[str] = None, verbose: bool = False, **kwargs: Any - ) -> Dict[str, Any]: + self, input_data: dict[str, Any], *, rule_set: str | None = None, verbose: bool = False, **kwargs: Any + ) -> dict[str, Any]: """Apply the rules and return results. For each rule group of a given rule set, rules are applied sequentially, @@ -177,7 +179,7 @@ def apply_rules( raise KeyError("'input_data' couldn't be empty.") # Var init. - input_data_copy: Dict[str, Any] = copy.deepcopy(input_data) + input_data_copy: dict[str, Any] = copy.deepcopy(input_data) # Prepare the result key input_data_copy["output"] = {} @@ -194,7 +196,7 @@ def apply_rules( ) # Var init. - results_dict: Dict[str, Any] = {"verbosity": {"rule_set": rule_set, "results": []}} + results_dict: dict[str, Any] = {"verbosity": {"rule_set": rule_set, "results": []}} # Groups' loop for group_id, rules_list in self.rules[rule_set].items(): @@ -227,7 +229,7 @@ def apply_rules( return results_dict @staticmethod - def _get_object_from_source_modules(module_list: List[str]) -> Dict[str, Any]: + def _get_object_from_source_modules(module_list: list[str]) -> dict[str, Any]: """(Protected) Collect all functions defined in the list of modules. @@ -237,29 +239,29 @@ def _get_object_from_source_modules(module_list: List[str]) -> Dict[str, Any]: Returns: Dictionary with objects found in the modules. """ - object_dict: Dict[str, Any] = {} + object_dict: dict[str, Any] = {} for module_name in module_list: # Import module mod: ModuleType = importlib.import_module(module_name) # Collect functions - module_functions: Dict[str, Any] = {key: val for key, val in getmembers(mod, isfunction)} + module_functions: dict[str, Any] = {key: val for key, val in getmembers(mod, isfunction)} object_dict.update(module_functions) # Collect classes - module_classes: Dict[str, Any] = {key: val for key, val in getmembers(mod, isclass)} + module_classes: dict[str, Any] = {key: val for key, val in getmembers(mod, isclass)} object_dict.update(module_classes) return object_dict def _build_rules( self, - std_condition_instances: Dict[str, StandardCondition], - action_functions: Dict[str, Callable], - config: Dict[str, Any], - factory_mapping_classes: Dict[str, Type[BaseCondition]], - ) -> Dict[str, Dict[str, List[Any]]]: + std_condition_instances: dict[str, StandardCondition], + action_functions: dict[str, Callable], + config: dict[str, Any], + factory_mapping_classes: dict[str, type[BaseCondition]], + ) -> dict[str, dict[str, list[Any]]]: """(Protected) Return a dictionary of Rule instances built from the configuration. @@ -274,16 +276,16 @@ def _build_rules( A dictionary of rules. """ # Var init. - rules_dict: Dict[str, Dict[str, List[Any]]] = {} + rules_dict: dict[str, dict[str, list[Any]]] = {} # Retrieve rule set ids from config - rule_set_ids: List[str] = list(config[self.CONST_RULE_SETS_CONF_KEY].keys()) + rule_set_ids: list[str] = list(config[self.CONST_RULE_SETS_CONF_KEY].keys()) # Going all way down to the rules (rule set > rule group > rule) for set_id in rule_set_ids: - rules_conf: Dict[str, Any] = config[self.CONST_RULE_SETS_CONF_KEY][set_id] + rules_conf: dict[str, Any] = config[self.CONST_RULE_SETS_CONF_KEY][set_id] rules_dict[set_id] = {} - rule_set_dict: Dict[str, List[Any]] = rules_dict[set_id] + rule_set_dict: dict[str, list[Any]] = rules_dict[set_id] # Looping throught groups for group_id, group_rules in rules_conf.items(): @@ -301,13 +303,13 @@ def _build_rules( action: Callable = action_functions[action_function_name] # Look for condition conf. keys inside the rule - condition_conf_keys: Set[str] = set(rule_dict.keys()) - { + condition_conf_keys: set[str] = set(rule_dict.keys()) - { self.CONST_ACTION_CONF_KEY, self.CONST_ACTION_PARAMETERS_CONF_KEY, } # Store the cond. expressions with the same order as in the configuration file (very important) - condition_exprs: Dict[str, Optional[str]] = { + condition_exprs: dict[str, str | None] = { key: value for key, value in rule_dict.items() if key in condition_conf_keys } @@ -327,8 +329,8 @@ def _build_rules( return rules_dict def _build_std_conditions( - self, config: Dict[str, Any], condition_functions_dict: Dict[str, Callable] - ) -> Dict[str, StandardCondition]: + self, config: dict[str, Any], condition_functions_dict: dict[str, Callable] + ) -> dict[str, StandardCondition]: """(Protected) Return a dictionary of Condition instances built from the configuration file. @@ -340,10 +342,10 @@ def _build_std_conditions( A dictionary of StandardCondition instances (k: condition id, v: StandardCondition instance). """ # Var init. - conditions_dict: Dict[str, StandardCondition] = {} + conditions_dict: dict[str, StandardCondition] = {} # Condition configuration (under conditions' key) - conditions_conf: Dict[str, Dict[str, Any]] = config[self.CONST_STD_CONDITIONS_CONF_KEY] + conditions_conf: dict[str, dict[str, Any]] = config[self.CONST_STD_CONDITIONS_CONF_KEY] # Looping through conditions (inside a group) for condition_id, condition_params in conditions_conf.items(): @@ -367,7 +369,7 @@ def _build_std_conditions( return conditions_dict - def _adapt_user_rules_dict(self, rules_dict: Dict[str, Dict[str, Any]]) -> Dict[str, Dict[str, List[Any]]]: + def _adapt_user_rules_dict(self, rules_dict: dict[str, dict[str, Any]]) -> dict[str, dict[str, list[Any]]]: """(Protected) Return a dictionary of Rule's instances built from user's rules dictionary. @@ -378,7 +380,7 @@ def _adapt_user_rules_dict(self, rules_dict: Dict[str, Dict[str, Any]]) -> Dict[ A rules dictionary made from the user input rules. """ # Var init. - rules_dict_formatted: Dict[str, List[Any]] = {} + rules_dict_formatted: dict[str, list[Any]] = {} # Looping throught groups for group_id, group_rules in rules_dict.items(): @@ -432,7 +434,7 @@ def __str__(self) -> str: # Get some instance attributes infos class_name: str = self.__class__.__name__ - attrs: List[Tuple[str, Any]] = [ + attrs: list[tuple[str, Any]] = [ attr for attr in inspect.getmembers(self) if not ( diff --git a/src/arta/condition.py b/src/arta/condition.py index 233e3ae..c616d9a 100644 --- a/src/arta/condition.py +++ b/src/arta/condition.py @@ -3,9 +3,11 @@ Classes: BaseCondition, StandardCondition, SimpleCondition """ +from __future__ import annotations + import re from abc import ABC, abstractmethod -from typing import Any, Callable, Dict, List, Optional, Set +from typing import Any, Callable from arta.exceptions import ConditionExecutionError from arta.utils import UPPERCASE_WORD_PATTERN, ParsingErrorStrategy, parse_dynamic_parameter @@ -31,8 +33,8 @@ def __init__( self, condition_id: str, description: str, - validation_function: Optional[Callable] = None, - validation_function_parameters: Optional[Dict[str, Any]] = None, + validation_function: Callable | None = None, + validation_function_parameters: dict[str, Any] | None = None, ) -> None: """ Initialize attributes. @@ -49,7 +51,7 @@ def __init__( self._validation_function_parameters = validation_function_parameters # NOSONAR @classmethod - def extract_condition_ids_from_expression(cls, condition_expr: Optional[str] = None) -> Set[str]: + def extract_condition_ids_from_expression(cls, condition_expr: str | None = None) -> set[str]: """Get the condition ids from a string (e.g., UPPERCASE words). E.g., CONDITION_1 and not CONDITION_2 @@ -62,7 +64,7 @@ def extract_condition_ids_from_expression(cls, condition_expr: Optional[str] = N Returns: A set of extracted condition ids. """ - cond_ids: Set[str] = set() + cond_ids: set[str] = set() if condition_expr is not None: cond_ids = set(re.findall(cls.CONDITION_ID_PATTERN, condition_expr)) @@ -70,7 +72,7 @@ def extract_condition_ids_from_expression(cls, condition_expr: Optional[str] = N return cond_ids @abstractmethod - def verify(self, input_data: Dict[str, Any], parsing_error_strategy: ParsingErrorStrategy, **kwargs: Any) -> bool: + def verify(self, input_data: dict[str, Any], parsing_error_strategy: ParsingErrorStrategy, **kwargs: Any) -> bool: """(Abstract) Return True if the condition is verified. @@ -95,7 +97,7 @@ class StandardCondition(BaseCondition): validation_function_parameters: Arguments of the validation function. """ - def verify(self, input_data: Dict[str, Any], parsing_error_strategy: ParsingErrorStrategy, **kwargs: Any) -> bool: + def verify(self, input_data: dict[str, Any], parsing_error_strategy: ParsingErrorStrategy, **kwargs: Any) -> bool: """Return True if the condition is verified. Example of a unitary standard condition: CONDITION_1 @@ -118,7 +120,7 @@ def verify(self, input_data: Dict[str, Any], parsing_error_strategy: ParsingErro raise AttributeError("Validation function parameters should not be None") # Parse dynamic parameters - parameters: Dict[str, Any] = {} + parameters: dict[str, Any] = {} for key, value in self._validation_function_parameters.items(): parameters[key] = parse_dynamic_parameter( @@ -143,7 +145,7 @@ class SimpleCondition(BaseCondition): CONST_CUSTOM_CONDITION_DATA_LABEL: str = "Simple condition data (not needed)" CONDITION_ID_PATTERN: str = r"(?:input\.|output\.)(?:[a-z_\-0-9!=<>\"NTF\.]*)" - def verify(self, input_data: Dict[str, Any], parsing_error_strategy: ParsingErrorStrategy, **kwargs: Any) -> bool: + def verify(self, input_data: dict[str, Any], parsing_error_strategy: ParsingErrorStrategy, **kwargs: Any) -> bool: """Return True if the condition is verified. Example of a unitary simple condition to be verified: 'input.age>=100' @@ -165,7 +167,7 @@ def verify(self, input_data: Dict[str, Any], parsing_error_strategy: ParsingErro data_path_patt: str = r"(?:input\.|output\.)(?:[a-z_\-\.]*)" # Retrieve only the data path - path_matches: List[str] = re.findall(data_path_patt, unitary_expr) + path_matches: list[str] = re.findall(data_path_patt, unitary_expr) if len(path_matches) == 1: # Regular case: we have a data_path diff --git a/src/arta/config.py b/src/arta/config.py index 6cbd709..ab3ecd3 100644 --- a/src/arta/config.py +++ b/src/arta/config.py @@ -1,12 +1,14 @@ """Configuration handling module.""" +from __future__ import annotations + from pathlib import Path -from typing import Any, Dict, List, Union, cast +from typing import Any, cast from omegaconf import DictConfig, ListConfig, OmegaConf -def load_config(config_dir_path: str) -> Dict[str, Any]: +def load_config(config_dir_path: str) -> dict[str, Any]: """Load a configuration dictionary from all the yaml files in a given directory (and its subdirectories). Args: @@ -17,9 +19,9 @@ def load_config(config_dir_path: str) -> Dict[str, Any]: Returns: config: Loaded config dictionary. """ - conf_files: List[Path] = [f for patt in ["*.yml", "*.yaml"] for f in Path(config_dir_path).rglob(patt)] - omega_config: Union[DictConfig, ListConfig] = OmegaConf.unsafe_merge(*[OmegaConf.load(file) for file in conf_files]) + conf_files: list[Path] = [f for patt in ["*.yml", "*.yaml"] for f in Path(config_dir_path).rglob(patt)] + omega_config: DictConfig | ListConfig = OmegaConf.unsafe_merge(*[OmegaConf.load(file) for file in conf_files]) - config: Dict[str, Any] = cast(Dict[str, Any], OmegaConf.to_object(omega_config)) + config: dict[str, Any] = cast(dict[str, Any], OmegaConf.to_object(omega_config)) return config diff --git a/src/arta/models.py b/src/arta/models.py index 94d29e5..e9d2339 100644 --- a/src/arta/models.py +++ b/src/arta/models.py @@ -1,4 +1,7 @@ -"""Pydantic model implementations.""" +"""Pydantic model implementations. + +Note: Having no "from __future__ import annotations" here is wanted (pydantic compatibility). +""" from typing import Any, Callable, Dict, List, Optional diff --git a/src/arta/rule.py b/src/arta/rule.py index 5aa1f34..3acd86e 100644 --- a/src/arta/rule.py +++ b/src/arta/rule.py @@ -3,8 +3,10 @@ Class: Rule """ +from __future__ import annotations + import re -from typing import Any, Callable, Dict, Optional, Set, Tuple, Type +from typing import Any, Callable from arta.condition import BaseCondition, StandardCondition from arta.exceptions import ConditionExecutionError, RuleExecutionError @@ -34,11 +36,11 @@ def __init__( set_id: str, group_id: str, rule_id: str, - condition_exprs: Dict[str, Optional[str]], - condition_factory_mapping: Dict[str, Type[BaseCondition]], + condition_exprs: dict[str, str | None], + condition_factory_mapping: dict[str, type[BaseCondition]], action: Callable, - std_condition_instances: Dict[str, StandardCondition], - action_parameters: Optional[Dict[str, Any]] = None, + std_condition_instances: dict[str, StandardCondition], + action_parameters: dict[str, Any] | None = None, ) -> None: """Initialize attributes. @@ -62,15 +64,15 @@ def __init__( self._condition_factory_mapping = condition_factory_mapping # Condition instances (k: condition id (not conf key), v: instances) - self._condition_instances: Dict[str, BaseCondition] = self._instantiate_conditions(std_condition_instances) + self._condition_instances: dict[str, BaseCondition] = self._instantiate_conditions(std_condition_instances) def apply( self, - input_data: Dict[str, Any], + input_data: dict[str, Any], *, parsing_error_strategy: ParsingErrorStrategy, **kwargs: Any, - ) -> Tuple[Optional[Any], Dict[str, Any]]: + ) -> tuple[Any | None, dict[str, Any]]: """Apply the rule on the input data, return action output (optional). Args: @@ -86,7 +88,7 @@ def apply( """ # If rule conditions are verified, the action is executed w/ the parameters' value is_conditions_ok: bool - rule_results: Dict[str, Any] + rule_results: dict[str, Any] is_conditions_ok, rule_results = self._check_conditions( input_data, parsing_error_strategy=parsing_error_strategy, **kwargs @@ -95,7 +97,7 @@ def apply( if is_conditions_ok: try: # Parse dynamic parameters - parameters: Dict[str, Any] = {} + parameters: dict[str, Any] = {} for key, value in self._action_parameters.items(): parameters[key] = parse_dynamic_parameter( parameter=value, input_data=input_data, parsing_error_strategy=parsing_error_strategy @@ -115,8 +117,8 @@ def apply( return None, {} def _check_conditions( - self, input_data: Dict[str, Any], parsing_error_strategy: ParsingErrorStrategy, **kwargs: Any - ) -> Tuple[bool, Dict[str, Any]]: + self, input_data: dict[str, Any], parsing_error_strategy: ParsingErrorStrategy, **kwargs: Any + ) -> tuple[bool, dict[str, Any]]: """(Protected) Return True if all conditions are verified. @@ -130,11 +132,11 @@ def _check_conditions( """ # Var init. all_conditions_res: bool = True - condition_results: Dict[str, Any] = {"rule_group": self._group_id, "verified_conditions": {}} + condition_results: dict[str, Any] = {"rule_group": self._group_id, "verified_conditions": {}} # Loop among condition expressions for cond_conf_key, expr in self._condition_exprs.items(): - condition_class: Type[BaseCondition] = self._condition_factory_mapping[cond_conf_key] + condition_class: type[BaseCondition] = self._condition_factory_mapping[cond_conf_key] # Evaluate the condition expression try: @@ -164,12 +166,12 @@ def _check_conditions( def _evaluate_condition_expr( self, - input_data: Dict[str, Any], - condition_class: Type[BaseCondition], + input_data: dict[str, Any], + condition_class: type[BaseCondition], parsing_error_strategy: ParsingErrorStrategy, - condition_expr: Optional[str] = None, + condition_expr: str | None = None, **kwargs: Any, - ) -> Tuple[bool, Dict[str, bool]]: + ) -> tuple[bool, dict[str, bool]]: """(Protected) Evaluate the condition expr (a boolean expression) and return the result (a boolean). @@ -188,7 +190,7 @@ def _evaluate_condition_expr( ConditionExecutionError: Error during condition execution. """ # Var init. - unitary_results: Dict[str, bool] = {} + unitary_results: dict[str, bool] = {} # Case of null condition expressions => Always True if condition_expr is None: @@ -198,7 +200,7 @@ def _evaluate_condition_expr( bool_expr: str = condition_expr # Extract condition ids from the expression - condition_ids: Set[str] = condition_class.extract_condition_ids_from_expression(condition_expr) + condition_ids: set[str] = condition_class.extract_condition_ids_from_expression(condition_expr) # Loop among the conditions of the expression # Verify the unitary condition @@ -224,8 +226,8 @@ def _evaluate_condition_expr( def _instantiate_conditions( self, - std_conditions: Dict[str, StandardCondition], - ) -> Dict[str, BaseCondition]: + std_conditions: dict[str, StandardCondition], + ) -> dict[str, BaseCondition]: """Parse condition expressions and build corresponding instances. E.g., for one condition: @@ -243,8 +245,8 @@ def _instantiate_conditions( KeyError: Condition id is unknown. """ # Var init. - condition_ids: Set[str] = set() - cond_instances: Dict[str, BaseCondition] = {} + condition_ids: set[str] = set() + cond_instances: dict[str, BaseCondition] = {} # Get all condition ids from the expressions (1 or many) for conf_key, expr in self._condition_exprs.items(): diff --git a/src/arta/utils.py b/src/arta/utils.py index d24f523..40fc1bb 100644 --- a/src/arta/utils.py +++ b/src/arta/utils.py @@ -1,9 +1,11 @@ """Utility module.""" +from __future__ import annotations + import copy import re from enum import Enum -from typing import Any, Dict, List, Tuple +from typing import Any # Global constants UPPERCASE_WORD_PATTERN: str = r"\b[A-Z_0-9]+\b" @@ -17,7 +19,7 @@ class ParsingErrorStrategy(str, Enum): DEFAULT_VALUE: str = "default_value" -def get_value_in_nested_dict_from_path(path: str, nested_dict: Dict[str, Any]) -> Any: +def get_value_in_nested_dict_from_path(path: str, nested_dict: dict[str, Any]) -> Any: """From a path, get a value in a nested dict. Ex: @@ -31,10 +33,10 @@ def get_value_in_nested_dict_from_path(path: str, nested_dict: Dict[str, Any]) - Returns: Found value. """ - keys: List[str] = path.split(".") + keys: list[str] = path.split(".") # Initialize value with whole nested dict - value: Dict[str, Any] = nested_dict + value: dict[str, Any] = nested_dict # Loop on path keys for key in keys: @@ -47,7 +49,7 @@ def get_value_in_nested_dict_from_path(path: str, nested_dict: Dict[str, Any]) - def parse_dynamic_parameter( parameter: Any, - input_data: Dict[str, Any], + input_data: dict[str, Any], parsing_error_strategy: ParsingErrorStrategy, ) -> Any: """Parse the value of parameterized parameters. @@ -100,7 +102,7 @@ def parse_dynamic_parameter( def check_parsing_error_strategy_override( param_path: str, parsing_error_strategy: ParsingErrorStrategy -) -> Tuple[Any, str, ParsingErrorStrategy]: +) -> tuple[Any, str, ParsingErrorStrategy]: """Check if the input parameter contains a flag to override the parsing error strategy. The following override syntaxes are accepted: diff --git a/tests/examples/code/actions.py b/tests/examples/code/actions.py index 8d645cd..9a4be3e 100644 --- a/tests/examples/code/actions.py +++ b/tests/examples/code/actions.py @@ -4,15 +4,17 @@ N.B: They are only demo functions. """ -from typing import Any, Dict, List +from __future__ import annotations +from typing import Any -def set_admission(value: bool, **kwargs: Any) -> Dict[str, bool]: + +def set_admission(value: bool, **kwargs: Any) -> dict[str, bool]: """Return a dictionary containing the admission result.""" return {"admission": value} -def set_student_course(course_id: str, **kwargs: Any) -> Dict[str, str]: +def set_student_course(course_id: str, **kwargs: Any) -> dict[str, str]: """Return the course id as a dictionary.""" return {"course_id": course_id} @@ -28,7 +30,7 @@ def send_email(mail_to: str, mail_content: str, meal: str, **kwargs: Any) -> boo return is_ok -def concatenate_str(list_str: List[Any], **kwargs: Any) -> str: +def concatenate_str(list_str: list[Any], **kwargs: Any) -> str: """Demo function: return the concatenation of a list of string using input_data (two levels max).""" list_str = [str(element) for element in list_str] return "".join(list_str) diff --git a/tests/examples/code/conditions.py b/tests/examples/code/conditions.py index 25b7792..5ce89c5 100644 --- a/tests/examples/code/conditions.py +++ b/tests/examples/code/conditions.py @@ -4,16 +4,16 @@ N.B: They are only demo functions. """ -from typing import List, Optional +from __future__ import annotations -def has_authorized_super_power(authorized_powers: List[str], candidate_powers: List[str]) -> bool: +def has_authorized_super_power(authorized_powers: list[str], candidate_powers: list[str]) -> bool: """Check candidate's powers and return True if OK.""" auth_powers = [power for power in authorized_powers if candidate_powers is not None and power in candidate_powers] return len(auth_powers) > 0 -def is_age_unknown(age: Optional[int]) -> bool: +def is_age_unknown(age: int | None) -> bool: """Check if age is unknown = return True if age is None.""" return age is None diff --git a/tests/examples/code/custom_class.py b/tests/examples/code/custom_class.py index 63a606d..a728d09 100644 --- a/tests/examples/code/custom_class.py +++ b/tests/examples/code/custom_class.py @@ -3,7 +3,9 @@ Custom classes are """ -from typing import Any, Dict +from __future__ import annotations + +from typing import Any from arta.condition import BaseCondition from arta.utils import ParsingErrorStrategy @@ -19,7 +21,7 @@ class CustomCondition(BaseCondition): A condition class must inherit from BaseCondition. """ - def verify(self, input_data: Dict[str, Any], parsing_error_strategy: ParsingErrorStrategy, **kwargs: Any) -> bool: + def verify(self, input_data: dict[str, Any], parsing_error_strategy: ParsingErrorStrategy, **kwargs: Any) -> bool: """ Return True if the condition is verified. diff --git a/tests/test_example_code/test_actions.py b/tests/test_example_code/test_actions.py index 6f61a0e..f05c98e 100644 --- a/tests/test_example_code/test_actions.py +++ b/tests/test_example_code/test_actions.py @@ -40,7 +40,7 @@ def test_set_student_course(course_id, expected): "roast beef", True, ), - ("suppercook@dummy.fr", "Thanks for preparing the following meal: ", "pancakes", True), + ("supercook@dummy.fr", "Thanks for preparing the following meal: ", "pancakes", True), ], ) def test_send_email(mail_to, mail_content, meal, expected):