diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 000000000..1137e2fc7 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,10 @@ +{ + "extends": "airbnb-base", + "parser": "babel-eslint", + "env": { + "browser": true + }, + "rules": { + "import/no-extraneous-dependencies": ["error", {"devDependencies": true, "optionalDependencies": true, "peerDependencies": true}] + } +} diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 6e8a87a98..38bf2df8d 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,3 +1,5 @@ +:warning: If your issue is about an error during fetching a link, please read: http://doc.wallabag.org/en/master/user/errors_during_fetching.html#how-can-i-help-to-fix-that + ### Issue details Please provide issue details here. diff --git a/.gitignore b/.gitignore index f596c7b8f..32b0fbbb5 100644 --- a/.gitignore +++ b/.gitignore @@ -21,8 +21,10 @@ /vendor/ # Assets and user uploads -/web/bundles/ -/web/uploads/ +web/uploads/ +!web/bundles +web/bundles/* +!web/bundles/wallabagcore # Build /app/build @@ -41,3 +43,10 @@ docker/data/ # To avoid crazy stuff on some PR, we must manually FORCE ADD IT on each new release composer.lock + +# assets stuff +node_modules/ +bin +app/Resources/build/ +!/src/Wallabag/CoreBundle/Resources/public +/src/Wallabag/CoreBundle/Resources/public/* diff --git a/.stylelintrc b/.stylelintrc new file mode 100644 index 000000000..40db42c66 --- /dev/null +++ b/.stylelintrc @@ -0,0 +1,3 @@ +{ + "extends": "stylelint-config-standard" +} diff --git a/.travis.yml b/.travis.yml index 53cf9bb53..c7bb05fb1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,9 @@ language: php +services: + - rabbitmq + - redis + # faster builds on docker-container setup sudo: false @@ -15,6 +19,9 @@ cache: directories: - vendor - $HOME/.composer/cache + - node_modules + - $HOME/.cache/bower + - $HOME/.npm php: - 5.5 @@ -23,6 +30,9 @@ php: - 7.1 - nightly +node_js: + - "5" + env: - DB=mysql - DB=pgsql @@ -31,29 +41,9 @@ env: matrix: fast_finish: true include: - # driver for PostgreSQL currently unsupported by HHVM, requires 3rd party dependency - - php: hhvm-3.12 - sudo: required - dist: trusty - group: edge - env: DB=mysql - addons: - apt: - packages: - - mysql-server-5.6 - - mysql-client-core-5.6 - - mysql-client-5.6 - services: - - mysql - - php: hhvm-3.12 - sudo: required - dist: trusty - group: edge - env: DB=sqlite - php: 7.0 - env: CS_FIXER=run VALIDATE_TRANSLATION_FILE=run DB=sqlite + env: CS_FIXER=run VALIDATE_TRANSLATION_FILE=run ASSETS=build DB=sqlite allow_failures: - - php: hhvm-3.12 - php: 7.1 - php: nightly @@ -67,15 +57,22 @@ before_script: - if [[ ! $PHP = hhvm* ]]; then echo "memory_limit=-1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; fi; # xdebug isn't enable for PHP 7.1 - if [[ ! $PHP = hhvm* ]]; then phpenv config-rm xdebug.ini || echo "xdebug not available"; fi + - if [[ $PHP = 5.5 ]]; then composer require "phpunit/phpunit:4.*" --no-update; fi; - composer self-update --no-progress - - if [[ "$DB" = "pgsql" ]]; then psql -c 'create database wallabag_test;' -U postgres; fi; + - if [[ $DB = pgsql ]]; then psql -c 'create database wallabag_test;' -U postgres; fi; + +install: + - if [[ $ASSETS = build ]]; then source ~/.nvm/nvm.sh && nvm install 6.7; fi; + - if [[ $ASSETS = build ]]; then npm install -g npm@latest; fi; + - if [[ $ASSETS = build ]]; then npm install; fi; before_install: - if [[ $TRAVIS_REPO_SLUG = wallabag/wallabag ]]; then cp .composer-auth.json ~/.composer/auth.json; fi; script: - - travis_wait composer install --no-interaction --no-progress --prefer-dist -o + - travis_wait bash composer install -o --no-interaction --no-progress --prefer-dist - ant prepare-$DB - - phpunit -v - - if [ "$CS_FIXER" = "run" ]; then php bin/php-cs-fixer fix src/ --verbose --dry-run ; fi; - - if [ "$VALIDATE_TRANSLATION_FILE" = "run" ]; then php bin/console lint:yaml src/Wallabag/CoreBundle/Resources/translations -v ; fi; + - if [[ $VALIDATE_TRANSLATION_FILE = '' ]]; then phpunit -v ; fi; + - if [[ $CS_FIXER = run ]]; then php bin/php-cs-fixer fix src/ --verbose --dry-run ; fi; + - if [[ $VALIDATE_TRANSLATION_FILE = run ]]; then php bin/console lint:yaml src/Wallabag/CoreBundle/Resources/translations -v ; fi; + - if [[ $ASSETS = build ]]; then ./node_modules/grunt-cli/bin/grunt tests; fi; diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f2c2655e..947dff6f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,193 +1,881 @@ # Changelog -All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [2.1.2](https://github.com/wallabag/wallabag/tree/2.1.2) (2016-10-17) -## [2.0.8] - 2016-09-07 +**Merged pull requests:** -### Added +- Fixed hardcoded title for internal settings [\#2464](https://github.com/wallabag/wallabag/pull/2464) ([nicosomb](https://github.com/nicosomb)) +- German: improve existing and add missing translation [\#2459](https://github.com/wallabag/wallabag/pull/2459) ([Strubbl](https://github.com/Strubbl)) +- add link to German documentation in about page [\#2457](https://github.com/wallabag/wallabag/pull/2457) ([Strubbl](https://github.com/Strubbl)) +- Fix tabs on material [\#2455](https://github.com/wallabag/wallabag/pull/2455) ([tcitworld](https://github.com/tcitworld)) +- Fix baggy display on small screens [\#2454](https://github.com/wallabag/wallabag/pull/2454) ([tcitworld](https://github.com/tcitworld)) +- Bring make dev [\#2451](https://github.com/wallabag/wallabag/pull/2451) ([tcitworld](https://github.com/tcitworld)) +- View improvements [\#2450](https://github.com/wallabag/wallabag/pull/2450) ([nicosomb](https://github.com/nicosomb)) +- Fixed french and german doc homepages [\#2447](https://github.com/wallabag/wallabag/pull/2447) ([nicosomb](https://github.com/nicosomb)) +- Added information about tagging rules in documentation [\#2446](https://github.com/wallabag/wallabag/pull/2446) ([nicosomb](https://github.com/nicosomb)) +- Mention example instance in docs [\#2444](https://github.com/wallabag/wallabag/pull/2444) ([Kaligule](https://github.com/Kaligule)) +- Minor fixes in the english documentation [\#2439](https://github.com/wallabag/wallabag/pull/2439) ([zertrin](https://github.com/zertrin)) +- Update messages.pl.yml [\#2438](https://github.com/wallabag/wallabag/pull/2438) ([mruminski](https://github.com/mruminski)) +- Added french documentation for upgrade [\#2435](https://github.com/wallabag/wallabag/pull/2435) ([nicosomb](https://github.com/nicosomb)) +- Added french documentation for parameters.yml [\#2434](https://github.com/wallabag/wallabag/pull/2434) ([nicosomb](https://github.com/nicosomb)) +- Update ISSUE\_TEMPLATE.md [\#2432](https://github.com/wallabag/wallabag/pull/2432) ([j0k3r](https://github.com/j0k3r)) +- Lock deps for FOSUser [\#2429](https://github.com/wallabag/wallabag/pull/2429) ([j0k3r](https://github.com/j0k3r)) +- Fix links on english documentation homepage [\#2426](https://github.com/wallabag/wallabag/pull/2426) ([nicosomb](https://github.com/nicosomb)) +- Fixed display for note in installation page [\#2422](https://github.com/wallabag/wallabag/pull/2422) ([nicosomb](https://github.com/nicosomb)) +- Avoid error when Redis isn't here in tests [\#2420](https://github.com/wallabag/wallabag/pull/2420) ([j0k3r](https://github.com/j0k3r)) +- Fixed Twitter Cards by adding a description tag [\#2419](https://github.com/wallabag/wallabag/pull/2419) ([nicosomb](https://github.com/nicosomb)) +- Added support of Twitter Cards for public articles [\#2418](https://github.com/wallabag/wallabag/pull/2418) ([nicosomb](https://github.com/nicosomb)) +- Define a dev version for the master [\#2417](https://github.com/wallabag/wallabag/pull/2417) ([j0k3r](https://github.com/j0k3r)) +- Remove automatic closing of the window from bookmarklet [\#2414](https://github.com/wallabag/wallabag/pull/2414) ([szafranek](https://github.com/szafranek)) +- When a sub command fail, display error message [\#2413](https://github.com/wallabag/wallabag/pull/2413) ([j0k3r](https://github.com/j0k3r)) +- Fix PostgreSQL migrations [\#2412](https://github.com/wallabag/wallabag/pull/2412) ([j0k3r](https://github.com/j0k3r)) +- Fix entities definition [\#2411](https://github.com/wallabag/wallabag/pull/2411) ([j0k3r](https://github.com/j0k3r)) +- Optimize tag list display [\#2410](https://github.com/wallabag/wallabag/pull/2410) ([j0k3r](https://github.com/j0k3r)) +- Occitan version update [\#2409](https://github.com/wallabag/wallabag/pull/2409) ([Quent-in](https://github.com/Quent-in)) +- Show number of annotations instead of nbAnnotations placeholder [\#2406](https://github.com/wallabag/wallabag/pull/2406) ([szafranek](https://github.com/szafranek)) +- Fix few invalid HTML tags [\#2405](https://github.com/wallabag/wallabag/pull/2405) ([szafranek](https://github.com/szafranek)) +- Cleaned up documentation for installation process [\#2403](https://github.com/wallabag/wallabag/pull/2403) ([nicosomb](https://github.com/nicosomb)) +- Removed 1.x stuff in CHANGELOG [\#2402](https://github.com/wallabag/wallabag/pull/2402) ([nicosomb](https://github.com/nicosomb)) +- Set env to prod in documentation [\#2400](https://github.com/wallabag/wallabag/pull/2400) ([j0k3r](https://github.com/j0k3r)) +- Use default locale for user config [\#2399](https://github.com/wallabag/wallabag/pull/2399) ([j0k3r](https://github.com/j0k3r)) +- Ensure orphan tag are remove in API [\#2397](https://github.com/wallabag/wallabag/pull/2397) ([j0k3r](https://github.com/j0k3r)) +- Update messages.pl.yml [\#2396](https://github.com/wallabag/wallabag/pull/2396) ([mruminski](https://github.com/mruminski)) +- Add ability to use socket [\#2395](https://github.com/wallabag/wallabag/pull/2395) ([j0k3r](https://github.com/j0k3r)) +- Ability to check multiple urls in API [\#2393](https://github.com/wallabag/wallabag/pull/2393) ([j0k3r](https://github.com/j0k3r)) +- Added default picture if preview picture is null [\#2389](https://github.com/wallabag/wallabag/pull/2389) ([nicosomb](https://github.com/nicosomb)) +- Fixed two-factor checkbox display in user admin panel [\#2388](https://github.com/wallabag/wallabag/pull/2388) ([nicosomb](https://github.com/nicosomb)) +- Changed Changelog by using github-changelog-generator from @skywinder [\#2386](https://github.com/wallabag/wallabag/pull/2386) ([nicosomb](https://github.com/nicosomb)) +- Added documentation about siteconfig fix [\#2385](https://github.com/wallabag/wallabag/pull/2385) ([nicosomb](https://github.com/nicosomb)) +- Added OpenGraph support for public articles [\#2383](https://github.com/wallabag/wallabag/pull/2383) ([nicosomb](https://github.com/nicosomb)) +- Fix exists API call [\#2377](https://github.com/wallabag/wallabag/pull/2377) ([tcitworld](https://github.com/tcitworld)) +- try to reduce assets build npm connection failing by updating nodejs [\#2375](https://github.com/wallabag/wallabag/pull/2375) ([tcitworld](https://github.com/tcitworld)) +- Clickable tags [\#2374](https://github.com/wallabag/wallabag/pull/2374) ([tcitworld](https://github.com/tcitworld)) +- API: ability to export entry in all available format \(epub, pdf, etc...\) [\#2372](https://github.com/wallabag/wallabag/pull/2372) ([pmartin](https://github.com/pmartin)) +- Remove mouf/nodejs-installer from composer [\#2363](https://github.com/wallabag/wallabag/pull/2363) ([j0k3r](https://github.com/j0k3r)) +- Changed relation between API client and refresh token [\#2351](https://github.com/wallabag/wallabag/pull/2351) ([nicosomb](https://github.com/nicosomb)) +- Fix relations export for Entry [\#2332](https://github.com/wallabag/wallabag/pull/2332) ([j0k3r](https://github.com/j0k3r)) +- Delete user [\#2177](https://github.com/wallabag/wallabag/pull/2177) ([tcitworld](https://github.com/tcitworld)) -- [#2262](https://github.com/wallabag/wallabag/pull/2262) Added a check for the database connection during installation (Jeremy Benoist) -- [#2235](https://github.com/wallabag/wallabag/pull/2235) Added configuration for german documentation website, [available here](http://doc.wallabag.org/de/latest/) (Nicolas Lœuillet) +## [2.1.1](https://github.com/wallabag/wallabag/tree/2.1.1) (2016-10-04) +**Merged pull requests:** -### Changed +- Create config even if user is disabled [\#2359](https://github.com/wallabag/wallabag/pull/2359) ([j0k3r](https://github.com/j0k3r)) +- Add php-bcmath extension to requirements [\#2354](https://github.com/wallabag/wallabag/pull/2354) ([Zayon](https://github.com/Zayon)) +- Basically, fix everything [\#2353](https://github.com/wallabag/wallabag/pull/2353) ([tcitworld](https://github.com/tcitworld)) +- Prepare wallabag 2.1.1 [\#2349](https://github.com/wallabag/wallabag/pull/2349) ([nicosomb](https://github.com/nicosomb)) +- Update messages.pl.yml [\#2341](https://github.com/wallabag/wallabag/pull/2341) ([mruminski](https://github.com/mruminski)) +- small improvement for german translation [\#2340](https://github.com/wallabag/wallabag/pull/2340) ([Strubbl](https://github.com/Strubbl)) +- Fix for 2.1 installation [\#2338](https://github.com/wallabag/wallabag/pull/2338) ([j0k3r](https://github.com/j0k3r)) -- [graby](https://github.com/j0k3r/graby/releases/tag/1.4.3) Update Graby version, which now handles ZIP files (Jeremy Benoist) -- [#2230](https://github.com/wallabag/wallabag/pull/2230) Changed title display in card view (Danilow Alexandr) +## [2.1.0](https://github.com/wallabag/wallabag/tree/2.1.0) (2016-10-03) +**Merged pull requests:** -### Fixed +- Docker : install PHP 'gd' extension [\#2319](https://github.com/wallabag/wallabag/pull/2319) ([pmartin](https://github.com/pmartin)) +- Update messages.pl.yml [\#2309](https://github.com/wallabag/wallabag/pull/2309) ([mruminski](https://github.com/mruminski)) +- Fix issue \#2296: epub export with special chars in the title. [\#2297](https://github.com/wallabag/wallabag/pull/2297) ([egilli](https://github.com/egilli)) +- Update messages.oc.yml [\#2290](https://github.com/wallabag/wallabag/pull/2290) ([Quent-in](https://github.com/Quent-in)) +- Fixed CSS for quickstart links [\#2335](https://github.com/wallabag/wallabag/pull/2335) ([nicosomb](https://github.com/nicosomb)) +- Prepare wallabag 2.1.0 :rocket: [\#2334](https://github.com/wallabag/wallabag/pull/2334) ([nicosomb](https://github.com/nicosomb)) +- for 2.1, translate all strings to German, which haven't been translated yet [\#2333](https://github.com/wallabag/wallabag/pull/2333) ([Strubbl](https://github.com/Strubbl)) +- Fix parameters in API \_links [\#2331](https://github.com/wallabag/wallabag/pull/2331) ([j0k3r](https://github.com/j0k3r)) +- Remove error message when creating ePub versions [\#2330](https://github.com/wallabag/wallabag/pull/2330) ([pmichelazzo](https://github.com/pmichelazzo)) +- Occitan version update [\#2329](https://github.com/wallabag/wallabag/pull/2329) ([Quent-in](https://github.com/Quent-in)) +- Avoid duplicate url with accents [\#2328](https://github.com/wallabag/wallabag/pull/2328) ([j0k3r](https://github.com/j0k3r)) +- Add users management UI [\#2327](https://github.com/wallabag/wallabag/pull/2327) ([j0k3r](https://github.com/j0k3r)) +- Changed quickstart layout [\#2326](https://github.com/wallabag/wallabag/pull/2326) ([nicosomb](https://github.com/nicosomb)) +- Add an exists endpoint in API [\#2325](https://github.com/wallabag/wallabag/pull/2325) ([j0k3r](https://github.com/j0k3r)) +- Add ability to edit a tagging rule [\#2324](https://github.com/wallabag/wallabag/pull/2324) ([j0k3r](https://github.com/j0k3r)) +- Add simple stats in footer [\#2323](https://github.com/wallabag/wallabag/pull/2323) ([j0k3r](https://github.com/j0k3r)) +- Customize errors templates [\#2322](https://github.com/wallabag/wallabag/pull/2322) ([j0k3r](https://github.com/j0k3r)) +- Create user config in one place. [\#2320](https://github.com/wallabag/wallabag/pull/2320) ([j0k3r](https://github.com/j0k3r)) +- Removed duplicated templates files [\#2318](https://github.com/wallabag/wallabag/pull/2318) ([nicosomb](https://github.com/nicosomb)) +- Update messages.pl.yml [\#2316](https://github.com/wallabag/wallabag/pull/2316) ([mruminski](https://github.com/mruminski)) +- Fixes about upgrade [\#2315](https://github.com/wallabag/wallabag/pull/2315) ([nicosomb](https://github.com/nicosomb)) +- Assets work [\#2314](https://github.com/wallabag/wallabag/pull/2314) ([tcitworld](https://github.com/tcitworld)) +- Remove CSS class who caused long loading on Firefox [\#2313](https://github.com/wallabag/wallabag/pull/2313) ([nicosomb](https://github.com/nicosomb)) +- Add Instapaper import [\#2310](https://github.com/wallabag/wallabag/pull/2310) ([j0k3r](https://github.com/j0k3r)) +- Fix duplicate tags on import [\#2308](https://github.com/wallabag/wallabag/pull/2308) ([j0k3r](https://github.com/j0k3r)) +- Some checks about Redis & RabbitMQ [\#2306](https://github.com/wallabag/wallabag/pull/2306) ([j0k3r](https://github.com/j0k3r)) +- fix feeds not syncing on android app [\#2301](https://github.com/wallabag/wallabag/pull/2301) ([tcitworld](https://github.com/tcitworld)) +- polish version [\#2299](https://github.com/wallabag/wallabag/pull/2299) ([mruminski](https://github.com/mruminski)) +- Fixed label for mark as read link in entry view [\#2292](https://github.com/wallabag/wallabag/pull/2292) ([nicosomb](https://github.com/nicosomb)) +- Add links to translations in documentation homepage [\#2291](https://github.com/wallabag/wallabag/pull/2291) ([nicosomb](https://github.com/nicosomb)) +- Remove footer part [\#2287](https://github.com/wallabag/wallabag/pull/2287) ([nicosomb](https://github.com/nicosomb)) +- Check if asset is enable instead of opposite [\#2279](https://github.com/wallabag/wallabag/pull/2279) ([j0k3r](https://github.com/j0k3r)) +- Export dates from entries [\#2275](https://github.com/wallabag/wallabag/pull/2275) ([j0k3r](https://github.com/j0k3r)) +- Re-use JsonResponse [\#2274](https://github.com/wallabag/wallabag/pull/2274) ([j0k3r](https://github.com/j0k3r)) +- Added tags counter in sidebar \(material theme\) [\#2266](https://github.com/wallabag/wallabag/pull/2266) ([nicosomb](https://github.com/nicosomb)) +- Instead of selecting the whole data, just count it [\#2263](https://github.com/wallabag/wallabag/pull/2263) ([j0k3r](https://github.com/j0k3r)) +- Fixed different font-size for labels in config screen [\#2260](https://github.com/wallabag/wallabag/pull/2260) ([nicosomb](https://github.com/nicosomb)) +- Update messages.pl.yml [\#2258](https://github.com/wallabag/wallabag/pull/2258) ([mruminski](https://github.com/mruminski)) +- Add Readability import [\#2255](https://github.com/wallabag/wallabag/pull/2255) ([j0k3r](https://github.com/j0k3r)) +- Fixed last merge who caused CSS/JS bugs [\#2252](https://github.com/wallabag/wallabag/pull/2252) ([nicosomb](https://github.com/nicosomb)) +- Changed where page title is displayed [\#2245](https://github.com/wallabag/wallabag/pull/2245) ([nicosomb](https://github.com/nicosomb)) +- Add untagged entries [\#2243](https://github.com/wallabag/wallabag/pull/2243) ([nicosomb](https://github.com/nicosomb)) +- Fix print / article views [\#2242](https://github.com/wallabag/wallabag/pull/2242) ([nicosomb](https://github.com/nicosomb)) +- Add icon for public share link [\#2240](https://github.com/wallabag/wallabag/pull/2240) ([nicosomb](https://github.com/nicosomb)) +- Update documentation with new features for 2.1 [\#2239](https://github.com/wallabag/wallabag/pull/2239) ([nicosomb](https://github.com/nicosomb)) +- Add original link in public view of the article [\#2237](https://github.com/wallabag/wallabag/pull/2237) ([nicosomb](https://github.com/nicosomb)) +- Delete tag or tags by label [\#2218](https://github.com/wallabag/wallabag/pull/2218) ([nicosomb](https://github.com/nicosomb)) +- Fix PHPunit 5 warnings [\#2201](https://github.com/wallabag/wallabag/pull/2201) ([j0k3r](https://github.com/j0k3r)) +- Import Firefox & Chrome bookmarks into wallabag [\#2192](https://github.com/wallabag/wallabag/pull/2192) ([tcitworld](https://github.com/tcitworld)) +- Add option to disable registration [\#2186](https://github.com/wallabag/wallabag/pull/2186) ([tcitworld](https://github.com/tcitworld)) +- Bring tags on entries view to baggy [\#2183](https://github.com/wallabag/wallabag/pull/2183) ([tcitworld](https://github.com/tcitworld)) +- Add since parameter [\#2176](https://github.com/wallabag/wallabag/pull/2176) ([tcitworld](https://github.com/tcitworld)) +- Add filter for tags on API [\#2174](https://github.com/wallabag/wallabag/pull/2174) ([tcitworld](https://github.com/tcitworld)) +- Name api clients [\#2171](https://github.com/wallabag/wallabag/pull/2171) ([tcitworld](https://github.com/tcitworld)) +- Add tags on entries view and minor ui fixes [\#2170](https://github.com/wallabag/wallabag/pull/2170) ([tcitworld](https://github.com/tcitworld)) +- fix font paths [\#2169](https://github.com/wallabag/wallabag/pull/2169) ([tcitworld](https://github.com/tcitworld)) +- Add modules when NodeJS versions are too low. [\#2168](https://github.com/wallabag/wallabag/pull/2168) ([tcitworld](https://github.com/tcitworld)) +- Manage assets through npm [\#2142](https://github.com/wallabag/wallabag/pull/2142) ([tcitworld](https://github.com/tcitworld)) +- Feature display items number [\#2002](https://github.com/wallabag/wallabag/pull/2002) ([nicosomb](https://github.com/nicosomb)) +- wallabag 2.1 [\#1999](https://github.com/wallabag/wallabag/pull/1999) ([nicosomb](https://github.com/nicosomb)) +- Add CLI import for wallabag v2 files [\#1990](https://github.com/wallabag/wallabag/pull/1990) ([nicosomb](https://github.com/nicosomb)) +- Links on each tag in Tags view [\#1943](https://github.com/wallabag/wallabag/pull/1943) ([nicosomb](https://github.com/nicosomb)) +- Use asynchronous jobs for imports [\#1941](https://github.com/wallabag/wallabag/pull/1941) ([nicosomb](https://github.com/nicosomb)) +- Share entry with a public URL [\#1904](https://github.com/wallabag/wallabag/pull/1904) ([nicosomb](https://github.com/nicosomb)) -- [#2234](https://github.com/wallabag/wallabag/pull/2234) Fixed mailto link in documentation (Christian Studer) -- [#2241](https://github.com/wallabag/wallabag/pull/2241) Fixed the height of the "Add new article" field in Chrome (Danilow Alexandr) -- [#2238](https://github.com/wallabag/wallabag/pull/2238) Fixed login page in Qupzilla (Danilow Alexandr) +## [2.0.8](https://github.com/wallabag/wallabag/tree/2.0.8) (2016-09-07) +**Merged pull requests:** -## [2.0.7] - 2016-08-22 +- Occitan updated [\#2267](https://github.com/wallabag/wallabag/pull/2267) ([Quent-in](https://github.com/Quent-in)) +- Allow failure for PHP 7.1 [\#2236](https://github.com/wallabag/wallabag/pull/2236) ([j0k3r](https://github.com/j0k3r)) +- Prepare wallabag 2.0.8 [\#2271](https://github.com/wallabag/wallabag/pull/2271) ([nicosomb](https://github.com/nicosomb)) +- Add a check for the database connection [\#2262](https://github.com/wallabag/wallabag/pull/2262) ([j0k3r](https://github.com/j0k3r)) +- Fix issue \#1991: correction of the height field to add articles [\#2241](https://github.com/wallabag/wallabag/pull/2241) ([modos189](https://github.com/modos189)) +- V2 improve view [\#2238](https://github.com/wallabag/wallabag/pull/2238) ([modos189](https://github.com/modos189)) +- Add configuration for german documentation [\#2235](https://github.com/wallabag/wallabag/pull/2235) ([nicosomb](https://github.com/nicosomb)) +- Fixes mailto link in documentation [\#2234](https://github.com/wallabag/wallabag/pull/2234) ([cstuder](https://github.com/cstuder)) +- Cut entries title in card view: continued [\#2230](https://github.com/wallabag/wallabag/pull/2230) ([modos189](https://github.com/modos189)) -### Added +## [2.0.7](https://github.com/wallabag/wallabag/tree/2.0.7) (2016-08-22) +**Merged pull requests:** -- [#2222](https://github.com/wallabag/wallabag/pull/2222) Added creation date and reading time on article view (Nicolas Lœuillet) -- [#2134](https://github.com/wallabag/wallabag/pull/2134) Run tests on an uptodate HHVM (Jeremy Benoist) +- Prepare wallabag 2.0.7 [\#2226](https://github.com/wallabag/wallabag/pull/2226) ([nicosomb](https://github.com/nicosomb)) +- Avoid breaking import when fetching fail [\#2224](https://github.com/wallabag/wallabag/pull/2224) ([j0k3r](https://github.com/j0k3r)) +- Added creation date and reading time on article view [\#2222](https://github.com/wallabag/wallabag/pull/2222) ([nicosomb](https://github.com/nicosomb)) +- Replaced favorite word/icon with star one [\#2221](https://github.com/wallabag/wallabag/pull/2221) ([nicosomb](https://github.com/nicosomb)) +- Enable PATCH method for CORS in API part [\#2220](https://github.com/wallabag/wallabag/pull/2220) ([Rurik19](https://github.com/Rurik19)) +- Enable CORS headers for OAUTH part [\#2216](https://github.com/wallabag/wallabag/pull/2216) ([Rurik19](https://github.com/Rurik19)) +- Run tests on an uptodate HHVM [\#2134](https://github.com/wallabag/wallabag/pull/2134) ([j0k3r](https://github.com/j0k3r)) +- Fix form user display when 2FA is disabled [\#2095](https://github.com/wallabag/wallabag/pull/2095) ([nicosomb](https://github.com/nicosomb)) -### Changed +## [2.0.6](https://github.com/wallabag/wallabag/tree/2.0.6) (2016-08-10) +**Merged pull requests:** -- [#2221](https://github.com/wallabag/wallabag/pull/2221) Replaced favorite word/icon with star one (Nicolas Lœuillet) +- add some missing phpdoc parameters [\#2202](https://github.com/wallabag/wallabag/pull/2202) ([tcitworld](https://github.com/tcitworld)) +- Run PHP 7.1 on Travis [\#2048](https://github.com/wallabag/wallabag/pull/2048) ([j0k3r](https://github.com/j0k3r)) +- Prepare release 2.0.6 [\#2212](https://github.com/wallabag/wallabag/pull/2212) ([nicosomb](https://github.com/nicosomb)) +- Fixed typo in entry:notice:entry\_saved [\#2200](https://github.com/wallabag/wallabag/pull/2200) ([charno6](https://github.com/charno6)) +- Handling socials links into a config file [\#2199](https://github.com/wallabag/wallabag/pull/2199) ([Simounet](https://github.com/Simounet)) +- FIX image inside a figure element max-width [\#2198](https://github.com/wallabag/wallabag/pull/2198) ([Simounet](https://github.com/Simounet)) +- Remove binary from repo [\#2195](https://github.com/wallabag/wallabag/pull/2195) ([j0k3r](https://github.com/j0k3r)) +- Fixed spelling Artúclos --\> Artículos [\#2194](https://github.com/wallabag/wallabag/pull/2194) ([benages](https://github.com/benages)) +- Fix 3rd-Party Apps links \(Chrome & Firefox\) [\#2185](https://github.com/wallabag/wallabag/pull/2185) ([tcitworld](https://github.com/tcitworld)) +- Change the way to login user in tests [\#2172](https://github.com/wallabag/wallabag/pull/2172) ([j0k3r](https://github.com/j0k3r)) +- Fix a few french translations typos [\#2165](https://github.com/wallabag/wallabag/pull/2165) ([tcitworld](https://github.com/tcitworld)) +- Update symlink to php-cs-fixer [\#2160](https://github.com/wallabag/wallabag/pull/2160) ([j0k3r](https://github.com/j0k3r)) +- Handle only upper or only lower reading filter [\#2157](https://github.com/wallabag/wallabag/pull/2157) ([j0k3r](https://github.com/j0k3r)) +- Try to find bad redirection after delete [\#2156](https://github.com/wallabag/wallabag/pull/2156) ([j0k3r](https://github.com/j0k3r)) +- Use friendsofphp instead of fabpot [\#2155](https://github.com/wallabag/wallabag/pull/2155) ([j0k3r](https://github.com/j0k3r)) +- translate documentation to German [\#2148](https://github.com/wallabag/wallabag/pull/2148) ([Strubbl](https://github.com/Strubbl)) +- Corrected Regex for lighttpd rewrite [\#2145](https://github.com/wallabag/wallabag/pull/2145) ([even-allmighty](https://github.com/even-allmighty)) +- Jump to Symfony 3.1 [\#2132](https://github.com/wallabag/wallabag/pull/2132) ([j0k3r](https://github.com/j0k3r)) -### Fixed +## [2.0.5](https://github.com/wallabag/wallabag/tree/2.0.5) (2016-05-31) +**Merged pull requests:** -- [#2224](https://github.com/wallabag/wallabag/pull/2224) Avoid breaking import when fetching fail (Jeremy Benoist) -- [#2216](https://github.com/wallabag/wallabag/pull/2216), [#2220](https://github.com/wallabag/wallabag/pull/2220) Enable CORS headers for OAUTH part (Rurik19) -- [#2095](https://github.com/wallabag/wallabag/pull/2095) Fix form user display when 2FA is disabled (Nicolas Lœuillet) +- Improve English translation [\#2109](https://github.com/wallabag/wallabag/pull/2109) ([Poorchop](https://github.com/Poorchop)) +- Update api.rst [\#2044](https://github.com/wallabag/wallabag/pull/2044) ([joshp23](https://github.com/joshp23)) +- new details in the doc about the rights access again;\) [\#2038](https://github.com/wallabag/wallabag/pull/2038) ([foxmask](https://github.com/foxmask)) +- Fix the deletion of Tags/Entries relation when delete an entry [\#2122](https://github.com/wallabag/wallabag/pull/2122) ([nicosomb](https://github.com/nicosomb)) +- Prepare wallabag 2.0.5 [\#2118](https://github.com/wallabag/wallabag/pull/2118) ([nicosomb](https://github.com/nicosomb)) +- Docs proposal [\#2112](https://github.com/wallabag/wallabag/pull/2112) ([Poorchop](https://github.com/Poorchop)) +- add screenshots of android docu in English [\#2111](https://github.com/wallabag/wallabag/pull/2111) ([Strubbl](https://github.com/Strubbl)) +- CS [\#2098](https://github.com/wallabag/wallabag/pull/2098) ([j0k3r](https://github.com/j0k3r)) +- Fix image path in 2-factor authentification email [\#2097](https://github.com/wallabag/wallabag/pull/2097) ([bmillemathias](https://github.com/bmillemathias)) +- Update CONTRIBUTING file [\#2094](https://github.com/wallabag/wallabag/pull/2094) ([nicosomb](https://github.com/nicosomb)) +- Replace vertical dots in material theme with horizontal dots [\#2093](https://github.com/wallabag/wallabag/pull/2093) ([nicosomb](https://github.com/nicosomb)) +- Starred and Archived clears if article is already exists [\#2092](https://github.com/wallabag/wallabag/pull/2092) ([Rurik19](https://github.com/Rurik19)) +- Do not specify language in Firefox addon link [\#2069](https://github.com/wallabag/wallabag/pull/2069) ([merwan](https://github.com/merwan)) +- Added information about permissions on data/ [\#2068](https://github.com/wallabag/wallabag/pull/2068) ([mariovor](https://github.com/mariovor)) +- Update CraueConfigBundle.it.yml [\#2054](https://github.com/wallabag/wallabag/pull/2054) ([jamiroconca](https://github.com/jamiroconca)) +- Add unread filter to entries pages [\#2052](https://github.com/wallabag/wallabag/pull/2052) ([danbartram](https://github.com/danbartram)) +- Update api.rst [\#2049](https://github.com/wallabag/wallabag/pull/2049) ([joshp23](https://github.com/joshp23)) -## [2.0.6] - 2016-08-10 +## [2.0.4](https://github.com/wallabag/wallabag/tree/2.0.4) (2016-05-07) +**Merged pull requests:** -### Changed +- Prepare wallabag 2.0.4 [\#2035](https://github.com/wallabag/wallabag/pull/2035) ([nicosomb](https://github.com/nicosomb)) +- Change Travis/Scrutinizer pictures in README [\#2029](https://github.com/wallabag/wallabag/pull/2029) ([nicosomb](https://github.com/nicosomb)) +- Docu for android app [\#2028](https://github.com/wallabag/wallabag/pull/2028) ([Strubbl](https://github.com/Strubbl)) +- Update messages.it.yml [\#2024](https://github.com/wallabag/wallabag/pull/2024) ([jamiroconca](https://github.com/jamiroconca)) +- Fix translation for validators [\#2023](https://github.com/wallabag/wallabag/pull/2023) ([nicosomb](https://github.com/nicosomb)) +- Fix pagination bar on small devices [\#2022](https://github.com/wallabag/wallabag/pull/2022) ([nicosomb](https://github.com/nicosomb)) +- Fix number of entries in tag/list [\#2020](https://github.com/wallabag/wallabag/pull/2020) ([nicosomb](https://github.com/nicosomb)) +- Create CraueConfigBundle.it.yml [\#2019](https://github.com/wallabag/wallabag/pull/2019) ([jamiroconca](https://github.com/jamiroconca)) +- Update config.yml, add italian as available language [\#2018](https://github.com/wallabag/wallabag/pull/2018) ([jamiroconca](https://github.com/jamiroconca)) +- Create messages.it.yml [\#2017](https://github.com/wallabag/wallabag/pull/2017) ([jamiroconca](https://github.com/jamiroconca)) +- Update documentation [\#2016](https://github.com/wallabag/wallabag/pull/2016) ([nicosomb](https://github.com/nicosomb)) +- Fix tags listing [\#2013](https://github.com/wallabag/wallabag/pull/2013) ([nicosomb](https://github.com/nicosomb)) +- integrate upgrade.rst [\#2012](https://github.com/wallabag/wallabag/pull/2012) ([biva](https://github.com/biva)) +- upgrade.rst \(Creation of an upgrade page in the documentation\) [\#2011](https://github.com/wallabag/wallabag/pull/2011) ([biva](https://github.com/biva)) +- Set the title via POST /api/entries [\#2010](https://github.com/wallabag/wallabag/pull/2010) ([nicosomb](https://github.com/nicosomb)) +- Fix reading speed not defined when user was created via config page [\#2005](https://github.com/wallabag/wallabag/pull/2005) ([nicosomb](https://github.com/nicosomb)) +- Fix old branch name urls [\#2001](https://github.com/wallabag/wallabag/pull/2001) ([tcitworld](https://github.com/tcitworld)) +- Update CraueConfigBundle.es.yml [\#1992](https://github.com/wallabag/wallabag/pull/1992) ([jami7](https://github.com/jami7)) +- Rights access to the folders of the project [\#1985](https://github.com/wallabag/wallabag/pull/1985) ([foxmask](https://github.com/foxmask)) +- Es translation [\#1977](https://github.com/wallabag/wallabag/pull/1977) ([j0k3r](https://github.com/j0k3r)) +- Fix filter reading time [\#1976](https://github.com/wallabag/wallabag/pull/1976) ([nicosomb](https://github.com/nicosomb)) +- Fix typos in API documentation [\#1970](https://github.com/wallabag/wallabag/pull/1970) ([nicosomb](https://github.com/nicosomb)) +- Create 3rd Resources chapter in API documentation [\#1969](https://github.com/wallabag/wallabag/pull/1969) ([nicosomb](https://github.com/nicosomb)) +- Add FAQ page in documentation [\#1967](https://github.com/wallabag/wallabag/pull/1967) ([nicosomb](https://github.com/nicosomb)) -- [#2199](https://github.com/wallabag/wallabag/pull/2199) Handling socials links into a config file (Simon Alberny) -- [#2172](https://github.com/wallabag/wallabag/pull/2172) Change the way to login user in tests (Jeremy Benoist) -- [#2155](https://github.com/wallabag/wallabag/pull/2155) Use friendsofphp instead of fabpot for PHP CS Fixer (Jeremy Benoist) +## [2.0.3](https://github.com/wallabag/wallabag/tree/2.0.3) (2016-04-22) +**Merged pull requests:** -### Fixed +- Prepare release 2.0.3 [\#1965](https://github.com/wallabag/wallabag/pull/1965) ([nicosomb](https://github.com/nicosomb)) +- Update API documentation with cURL examples [\#1962](https://github.com/wallabag/wallabag/pull/1962) ([nicosomb](https://github.com/nicosomb)) -- [#2200](https://github.com/wallabag/wallabag/pull/2200) Fixed typo in entry:notice:entry_saved (charno6) -- [#2185](https://github.com/wallabag/wallabag/pull/2185) Fix 3rd-Party Apps links (Chrome & Firefox) (Thomas Citharel) -- [#2165](https://github.com/wallabag/wallabag/pull/2165) Fix a few french translations typos (Thomas Citharel) -- [#2157](https://github.com/wallabag/wallabag/pull/2157) Handle only upper or only lower reading filter (Jeremy Benoist) -- [#2156](https://github.com/wallabag/wallabag/pull/2156) Try to find bad redirection after delete (Jeremy Benoist) +## [2.0.2](https://github.com/wallabag/wallabag/tree/2.0.2) (2016-04-21) +**Merged pull requests:** -## [2.0.5] - 2016-05-31 +- Update messages.es.yml [\#1935](https://github.com/wallabag/wallabag/pull/1935) ([jami7](https://github.com/jami7)) +- Fix UTF-8 encoding before JSON export [\#1866](https://github.com/wallabag/wallabag/pull/1866) ([ymage](https://github.com/ymage)) +- \[Do Not Merge\] Wallabag 1.9.2 [\#1814](https://github.com/wallabag/wallabag/pull/1814) ([tcitworld](https://github.com/tcitworld)) +- Fix translation for Go to your account button after subscription [\#1957](https://github.com/wallabag/wallabag/pull/1957) ([nicosomb](https://github.com/nicosomb)) +- Update links in documentation [\#1954](https://github.com/wallabag/wallabag/pull/1954) ([nicosomb](https://github.com/nicosomb)) +- Actualisation des liens morts \(Documentation de traduction\) [\#1953](https://github.com/wallabag/wallabag/pull/1953) ([maxi62330](https://github.com/maxi62330)) +- Prepare release 2.0.2 [\#1948](https://github.com/wallabag/wallabag/pull/1948) ([nicosomb](https://github.com/nicosomb)) +- Added some curl examples [\#1945](https://github.com/wallabag/wallabag/pull/1945) ([ddeimeke](https://github.com/ddeimeke)) +- Update Travis configuration with branches renaming [\#1944](https://github.com/wallabag/wallabag/pull/1944) ([nicosomb](https://github.com/nicosomb)) +- Optimize import [\#1942](https://github.com/wallabag/wallabag/pull/1942) ([nicosomb](https://github.com/nicosomb)) +- Display pagination only if we have more than one page [\#1938](https://github.com/wallabag/wallabag/pull/1938) ([nicosomb](https://github.com/nicosomb)) +- Hide pagination if we only have one entry to display [\#1933](https://github.com/wallabag/wallabag/pull/1933) ([nicosomb](https://github.com/nicosomb)) +- Change guid and link in RSS feeds to give original entry URL [\#1929](https://github.com/wallabag/wallabag/pull/1929) ([nicosomb](https://github.com/nicosomb)) +- Redirect to homepage if referer is null [\#1925](https://github.com/wallabag/wallabag/pull/1925) ([nicosomb](https://github.com/nicosomb)) +- Remove useless dependancy [\#1919](https://github.com/wallabag/wallabag/pull/1919) ([nicosomb](https://github.com/nicosomb)) +- Improve pagination when user has lot of entries [\#1918](https://github.com/wallabag/wallabag/pull/1918) ([nicosomb](https://github.com/nicosomb)) +- Convert array + phpDoc [\#1916](https://github.com/wallabag/wallabag/pull/1916) ([j0k3r](https://github.com/j0k3r)) +- Fix doc links [\#1915](https://github.com/wallabag/wallabag/pull/1915) ([j0k3r](https://github.com/j0k3r)) +- add title hover on entry titles [\#1911](https://github.com/wallabag/wallabag/pull/1911) ([tcitworld](https://github.com/tcitworld)) -### Added +## [2.0.1](https://github.com/wallabag/wallabag/tree/2.0.1) (2016-04-11) +**Merged pull requests:** -- [#2052](https://github.com/wallabag/wallabag/pull/2052) Add unread filter to entries pages (Dan Bartram) +- Fix estimated reading time in material view [\#1898](https://github.com/wallabag/wallabag/pull/1898) ([nicosomb](https://github.com/nicosomb)) +- Move setup config outside setupAdmin [\#1912](https://github.com/wallabag/wallabag/pull/1912) ([j0k3r](https://github.com/j0k3r)) +- Prepare release 2.0.1 [\#1909](https://github.com/wallabag/wallabag/pull/1909) ([nicosomb](https://github.com/nicosomb)) +- Force server version to avoid connexion error [\#1903](https://github.com/wallabag/wallabag/pull/1903) ([j0k3r](https://github.com/j0k3r)) +- Add changelog [\#1902](https://github.com/wallabag/wallabag/pull/1902) ([nicosomb](https://github.com/nicosomb)) +- Update Spanish translation [\#1901](https://github.com/wallabag/wallabag/pull/1901) ([j0k3r](https://github.com/j0k3r)) +- Remove empty portugese documentation [\#1900](https://github.com/wallabag/wallabag/pull/1900) ([nicosomb](https://github.com/nicosomb)) +- Add documentation about importing large file into nginx [\#1899](https://github.com/wallabag/wallabag/pull/1899) ([nicosomb](https://github.com/nicosomb)) +- Fix internal settings [\#1896](https://github.com/wallabag/wallabag/pull/1896) ([nicosomb](https://github.com/nicosomb)) +- All the entries are fetched via GET /api/entries [\#1895](https://github.com/wallabag/wallabag/pull/1895) ([nicosomb](https://github.com/nicosomb)) +- Fix documentation link in developer page [\#1894](https://github.com/wallabag/wallabag/pull/1894) ([nicosomb](https://github.com/nicosomb)) +- Fix typo on configuration page [\#1891](https://github.com/wallabag/wallabag/pull/1891) ([nicosomb](https://github.com/nicosomb)) +- Fix duplicate article when added via the bookmarklet [\#1887](https://github.com/wallabag/wallabag/pull/1887) ([nicosomb](https://github.com/nicosomb)) +- Change the installation method in issue template [\#1884](https://github.com/wallabag/wallabag/pull/1884) ([nicosomb](https://github.com/nicosomb)) +- Move tag form in Material theme [\#1879](https://github.com/wallabag/wallabag/pull/1879) ([nicosomb](https://github.com/nicosomb)) +- Remove keyboard shortcut and add tagging rule panel in baggy [\#1868](https://github.com/wallabag/wallabag/pull/1868) ([nicosomb](https://github.com/nicosomb)) +- Add production environment for CLI import [\#1862](https://github.com/wallabag/wallabag/pull/1862) ([nicosomb](https://github.com/nicosomb)) +- Round estimated time and add reading speed for Baggy [\#1861](https://github.com/wallabag/wallabag/pull/1861) ([nicosomb](https://github.com/nicosomb)) +- Documentation about wallabag v1 CLI import [\#1856](https://github.com/wallabag/wallabag/pull/1856) ([nicosomb](https://github.com/nicosomb)) +- English documentation about wallabag API [\#1854](https://github.com/wallabag/wallabag/pull/1854) ([nicosomb](https://github.com/nicosomb)) +- Lock ocramius/proxy-manager [\#1844](https://github.com/wallabag/wallabag/pull/1844) ([j0k3r](https://github.com/j0k3r)) +- Fix Archive page title translation [\#1840](https://github.com/wallabag/wallabag/pull/1840) ([nicosomb](https://github.com/nicosomb)) +- Remove SMTP configuration environment overrides [\#1824](https://github.com/wallabag/wallabag/pull/1824) ([nicosomb](https://github.com/nicosomb)) +- Translation fa [\#1823](https://github.com/wallabag/wallabag/pull/1823) ([mabkenar](https://github.com/mabkenar)) +- Force user-agent for iansommerville.com [\#1804](https://github.com/wallabag/wallabag/pull/1804) ([j0k3r](https://github.com/j0k3r)) -### Changed +## [2.0.0](https://github.com/wallabag/wallabag/tree/2.0.0) (2016-04-03) +**Merged pull requests:** -- [#2093](https://github.com/wallabag/wallabag/pull/2093) Replace vertical dots in material theme with horizontal dots (Nicolas Lœuillet) -- [#2054](https://github.com/wallabag/wallabag/pull/2054) Update italian translation (Daniele Conca) -- [#2068](https://github.com/wallabag/wallabag/pull/2068), [#2049](https://github.com/wallabag/wallabag/pull/2049) Update documentation (Josh Panter, Mario Vormstein) +- Occitan version [\#1829](https://github.com/wallabag/wallabag/pull/1829) ([Quent-in](https://github.com/Quent-in)) +- remove buggy swipe actions [\#1825](https://github.com/wallabag/wallabag/pull/1825) ([tcitworld](https://github.com/tcitworld)) +- Update messages.es.yml [\#1822](https://github.com/wallabag/wallabag/pull/1822) ([jami7](https://github.com/jami7)) +- Update messages.es.yml [\#1821](https://github.com/wallabag/wallabag/pull/1821) ([jami7](https://github.com/jami7)) +- Correct translation [\#1817](https://github.com/wallabag/wallabag/pull/1817) ([mruminski](https://github.com/mruminski)) +- Add message on unread page about v2 [\#1815](https://github.com/wallabag/wallabag/pull/1815) ([nicosomb](https://github.com/nicosomb)) +- \[v2\] German translation [\#1813](https://github.com/wallabag/wallabag/pull/1813) ([jlnostr](https://github.com/jlnostr)) +- fix typo diaspora\* share [\#1807](https://github.com/wallabag/wallabag/pull/1807) ([nikaro](https://github.com/nikaro)) +- Hide search buttons [\#1838](https://github.com/wallabag/wallabag/pull/1838) ([nicosomb](https://github.com/nicosomb)) +- :rocket: Prepare v2.0.0 :birthday: [\#1835](https://github.com/wallabag/wallabag/pull/1835) ([nicosomb](https://github.com/nicosomb)) +- Check selected PDO driver on install [\#1832](https://github.com/wallabag/wallabag/pull/1832) ([j0k3r](https://github.com/j0k3r)) +- Refacto wallabag import [\#1820](https://github.com/wallabag/wallabag/pull/1820) ([j0k3r](https://github.com/j0k3r)) +- Some cleanup [\#1819](https://github.com/wallabag/wallabag/pull/1819) ([j0k3r](https://github.com/j0k3r)) +- add migration docs [\#1818](https://github.com/wallabag/wallabag/pull/1818) ([tcitworld](https://github.com/tcitworld)) +- Translate flash message in baggy theme [\#1806](https://github.com/wallabag/wallabag/pull/1806) ([nicosomb](https://github.com/nicosomb)) +- Fix \#1768 Split tags with commas from UI [\#1805](https://github.com/wallabag/wallabag/pull/1805) ([nicosomb](https://github.com/nicosomb)) +- fix tabs display on mobile view [\#1799](https://github.com/wallabag/wallabag/pull/1799) ([tcitworld](https://github.com/tcitworld)) +- Don't add entry through API if it already exists [\#1797](https://github.com/wallabag/wallabag/pull/1797) ([tcitworld](https://github.com/tcitworld)) +- add tests on user in API responses [\#1792](https://github.com/wallabag/wallabag/pull/1792) ([tcitworld](https://github.com/tcitworld)) +- fix api properties and typo [\#1791](https://github.com/wallabag/wallabag/pull/1791) ([tcitworld](https://github.com/tcitworld)) +- API shows just what needed for user [\#1786](https://github.com/wallabag/wallabag/pull/1786) ([tcitworld](https://github.com/tcitworld)) +- add refresh-token grant-type [\#1784](https://github.com/wallabag/wallabag/pull/1784) ([tcitworld](https://github.com/tcitworld)) +- Switch to keys in translated files [\#1774](https://github.com/wallabag/wallabag/pull/1774) ([j0k3r](https://github.com/j0k3r)) +- Setting for reading time [\#1767](https://github.com/wallabag/wallabag/pull/1767) ([nicosomb](https://github.com/nicosomb)) +- reimport v1 entries if they were not fetched [\#1666](https://github.com/wallabag/wallabag/pull/1666) ([tcitworld](https://github.com/tcitworld)) +- lots of diverse translation stuff & some typos [\#1665](https://github.com/wallabag/wallabag/pull/1665) ([tcitworld](https://github.com/tcitworld)) +- remove tag from entry \#1377 [\#1663](https://github.com/wallabag/wallabag/pull/1663) ([tcitworld](https://github.com/tcitworld)) +- v2 – Fix some deprecated for Symfony3 [\#1510](https://github.com/wallabag/wallabag/pull/1510) ([j0k3r](https://github.com/j0k3r)) +- Fix quote strategy for reserved keyword in Postgres [\#1500](https://github.com/wallabag/wallabag/pull/1500) ([j0k3r](https://github.com/j0k3r)) +- Rule based tags [\#1478](https://github.com/wallabag/wallabag/pull/1478) ([K-Phoen](https://github.com/K-Phoen)) +- \[WIP\] language selection on config screen [\#1446](https://github.com/wallabag/wallabag/pull/1446) ([nicosomb](https://github.com/nicosomb)) +- Add multiple database tests on Travis [\#1434](https://github.com/wallabag/wallabag/pull/1434) ([j0k3r](https://github.com/j0k3r)) -### Fixed +## [2.0.0-beta.2](https://github.com/wallabag/wallabag/tree/2.0.0-beta.2) (2016-03-12) +**Merged pull requests:** -- [#2122](https://github.com/wallabag/wallabag/pull/2122) Fix the deletion of Tags/Entries relation when delete an entry (Jeremy Benoist, Nicolas Lœuillet) -- [#2095](https://github.com/wallabag/wallabag/pull/2095) Fix form user display when 2FA is disabled (Nicolas Lœuillet) -- [#2092](https://github.com/wallabag/wallabag/pull/2092) API: Starred and archived clears if article is already exists (Rurik19) -- [#2097](https://github.com/wallabag/wallabag/issues/2097) Fix image path in 2-factor authentification email (Baptiste Mille-Mathias) -- [#2069](https://github.com/wallabag/wallabag/pull/2069) Do not specify language in Firefox addon link (Merouane Atig) +- clarify that wllbg is compatible php 7 [\#1776](https://github.com/wallabag/wallabag/pull/1776) ([tcitworld](https://github.com/tcitworld)) +- Some fixes [\#1775](https://github.com/wallabag/wallabag/pull/1775) ([j0k3r](https://github.com/j0k3r)) +- Docker timezone [\#1771](https://github.com/wallabag/wallabag/pull/1771) ([j0k3r](https://github.com/j0k3r)) +- Fix \#1756: Added 'wallabag' in page title [\#1766](https://github.com/wallabag/wallabag/pull/1766) ([nicosomb](https://github.com/nicosomb)) +- Prepare files for 2.0.0-beta.2 [\#1763](https://github.com/wallabag/wallabag/pull/1763) ([nicosomb](https://github.com/nicosomb)) +- Change documentation links in quickstart [\#1762](https://github.com/wallabag/wallabag/pull/1762) ([nicosomb](https://github.com/nicosomb)) +- V2 api version [\#1761](https://github.com/wallabag/wallabag/pull/1761) ([tcitworld](https://github.com/tcitworld)) +- Add starred & archive properties to API [\#1754](https://github.com/wallabag/wallabag/pull/1754) ([j0k3r](https://github.com/j0k3r)) +- Doc: fix typo in php-hash ext name [\#1753](https://github.com/wallabag/wallabag/pull/1753) ([Horgix](https://github.com/Horgix)) +- Documentation about filters / Quickstart changes [\#1750](https://github.com/wallabag/wallabag/pull/1750) ([nicosomb](https://github.com/nicosomb)) +- V2 spanish translation [\#1745](https://github.com/wallabag/wallabag/pull/1745) ([benages](https://github.com/benages)) +- Releasing documentation [\#1743](https://github.com/wallabag/wallabag/pull/1743) ([nicosomb](https://github.com/nicosomb)) +- Add the way to install \(package or composer\) [\#1742](https://github.com/wallabag/wallabag/pull/1742) ([nicosomb](https://github.com/nicosomb)) +- minor typofix again [\#1740](https://github.com/wallabag/wallabag/pull/1740) ([goofy-bz](https://github.com/goofy-bz)) +- Ability to create new client for the API [\#1675](https://github.com/wallabag/wallabag/pull/1675) ([nicosomb](https://github.com/nicosomb)) +- Mark all imported articles as read [\#1670](https://github.com/wallabag/wallabag/pull/1670) ([tcitworld](https://github.com/tcitworld)) +- Added RELEASE\_PROCESS document [\#1615](https://github.com/wallabag/wallabag/pull/1615) ([bdunogier](https://github.com/bdunogier)) -## [2.0.4] - 2016-05-07 +## [2.0.0-beta.1](https://github.com/wallabag/wallabag/tree/2.0.0-beta.1) (2016-03-01) +**Merged pull requests:** -### Added +- minor typofix + missing quotes [\#1739](https://github.com/wallabag/wallabag/pull/1739) ([goofy-bz](https://github.com/goofy-bz)) +- Remove extra definition [\#1734](https://github.com/wallabag/wallabag/pull/1734) ([j0k3r](https://github.com/j0k3r)) +- Adding Danish translations to Wallabag v2 [\#1732](https://github.com/wallabag/wallabag/pull/1732) ([mjjzf](https://github.com/mjjzf)) +- Make English welcome message more natural [\#1723](https://github.com/wallabag/wallabag/pull/1723) ([shtrom](https://github.com/shtrom)) +- display how much comments there are for an entry [\#1722](https://github.com/wallabag/wallabag/pull/1722) ([tcitworld](https://github.com/tcitworld)) +- Change wording for create a new user [\#1714](https://github.com/wallabag/wallabag/pull/1714) ([j0k3r](https://github.com/j0k3r)) +- Create validators.pl.yml [\#1689](https://github.com/wallabag/wallabag/pull/1689) ([mruminski](https://github.com/mruminski)) +- Create messages.pl.yml [\#1688](https://github.com/wallabag/wallabag/pull/1688) ([mruminski](https://github.com/mruminski)) +- Translated another two files into romanian. [\#1687](https://github.com/wallabag/wallabag/pull/1687) ([0wnrepo](https://github.com/0wnrepo)) +- Polish translation [\#1686](https://github.com/wallabag/wallabag/pull/1686) ([mruminski](https://github.com/mruminski)) +- two more Persian translated files [\#1681](https://github.com/wallabag/wallabag/pull/1681) ([mabkenar](https://github.com/mabkenar)) +- Translation file for the 'Romanian' language. Also recognised by ro\_RO. Known in romanian as "Română". [\#1678](https://github.com/wallabag/wallabag/pull/1678) ([0wnrepo](https://github.com/0wnrepo)) +- Translation for Persian [\#1676](https://github.com/wallabag/wallabag/pull/1676) ([mabkenar](https://github.com/mabkenar)) +- improve baggy theme \(add new tag panel\) [\#1672](https://github.com/wallabag/wallabag/pull/1672) ([tcitworld](https://github.com/tcitworld)) +- display pictures properly [\#1671](https://github.com/wallabag/wallabag/pull/1671) ([tcitworld](https://github.com/tcitworld)) +- Add @GitterHQ badge on README [\#1630](https://github.com/wallabag/wallabag/pull/1630) ([nicosomb](https://github.com/nicosomb)) +- Add Turkish translation [\#1628](https://github.com/wallabag/wallabag/pull/1628) ([ogunkarakus](https://github.com/ogunkarakus)) +- Prepare wallabag for first beta :rocket: [\#1738](https://github.com/wallabag/wallabag/pull/1738) ([nicosomb](https://github.com/nicosomb)) +- Update Capistrano libraries with last versions [\#1736](https://github.com/wallabag/wallabag/pull/1736) ([nicosomb](https://github.com/nicosomb)) +- Fix: \#1703: authentication for PR by external contributors [\#1731](https://github.com/wallabag/wallabag/pull/1731) ([nicosomb](https://github.com/nicosomb)) +- Fix \#1717: Change remove tag icon in entry view with material theme [\#1728](https://github.com/wallabag/wallabag/pull/1728) ([nicosomb](https://github.com/nicosomb)) +- move annotatorjs to the bottom of the body so that the "article" tag is loaded before [\#1721](https://github.com/wallabag/wallabag/pull/1721) ([tcitworld](https://github.com/tcitworld)) +- Fix \#1603: Enhance requirements in InstallCommand [\#1716](https://github.com/wallabag/wallabag/pull/1716) ([nicosomb](https://github.com/nicosomb)) +- Fix \#1694 \#1669 \#1684: Add contributors and 3rd party libraries [\#1715](https://github.com/wallabag/wallabag/pull/1715) ([nicosomb](https://github.com/nicosomb)) +- Fix \#1711: Update CONTRIBUTING file [\#1712](https://github.com/wallabag/wallabag/pull/1712) ([nicosomb](https://github.com/nicosomb)) +- Fix \#811: Display a human message when graby fails to fetch articles [\#1710](https://github.com/wallabag/wallabag/pull/1710) ([nicosomb](https://github.com/nicosomb)) +- Fix \#1708: typos in french documentation [\#1709](https://github.com/wallabag/wallabag/pull/1709) ([nicosomb](https://github.com/nicosomb)) +- typo in french documentation [\#1707](https://github.com/wallabag/wallabag/pull/1707) ([nicosomb](https://github.com/nicosomb)) +- Documentation for wallabag installation [\#1706](https://github.com/wallabag/wallabag/pull/1706) ([nicosomb](https://github.com/nicosomb)) +- French translation for documentation [\#1705](https://github.com/wallabag/wallabag/pull/1705) ([nicosomb](https://github.com/nicosomb)) +- Added Piwik implementation [\#1702](https://github.com/wallabag/wallabag/pull/1702) ([nicosomb](https://github.com/nicosomb)) +- Fix \#1351: add maintenance mode [\#1701](https://github.com/wallabag/wallabag/pull/1701) ([nicosomb](https://github.com/nicosomb)) +- Fix \#1551 - Redirect to the last page when current page is out of range [\#1700](https://github.com/wallabag/wallabag/pull/1700) ([delyriand](https://github.com/delyriand)) +- Move assignTagsToEntry in ContentProxy helper [\#1699](https://github.com/wallabag/wallabag/pull/1699) ([j0k3r](https://github.com/j0k3r)) +- Add some missing french translations [\#1696](https://github.com/wallabag/wallabag/pull/1696) ([nicosomb](https://github.com/nicosomb)) +- Fix \#1691 create ISSUE\_TEMPLATE and PULL\_REQUEST\_TEMPLATE [\#1693](https://github.com/wallabag/wallabag/pull/1693) ([nicosomb](https://github.com/nicosomb)) +- translate estimated time minutes [\#1690](https://github.com/wallabag/wallabag/pull/1690) ([tcitworld](https://github.com/tcitworld)) +- Demonstration mode to prevent password changes [\#1683](https://github.com/wallabag/wallabag/pull/1683) ([nicosomb](https://github.com/nicosomb)) +- Update Capistrano configuration for symfony3 [\#1680](https://github.com/wallabag/wallabag/pull/1680) ([nicosomb](https://github.com/nicosomb)) +- Documentation about translation [\#1679](https://github.com/wallabag/wallabag/pull/1679) ([nicosomb](https://github.com/nicosomb)) +- Few updates [\#1668](https://github.com/wallabag/wallabag/pull/1668) ([j0k3r](https://github.com/j0k3r)) +- Update README.md with the environment parameters [\#1659](https://github.com/wallabag/wallabag/pull/1659) ([0wnrepo](https://github.com/0wnrepo)) +- import tags from v1 \(\#1657\) [\#1658](https://github.com/wallabag/wallabag/pull/1658) ([tcitworld](https://github.com/tcitworld)) +- fix labels on new user prompt [\#1656](https://github.com/wallabag/wallabag/pull/1656) ([tcitworld](https://github.com/tcitworld)) +- fix \#1636 [\#1654](https://github.com/wallabag/wallabag/pull/1654) ([tcitworld](https://github.com/tcitworld)) +- V2 annotator comments [\#1653](https://github.com/wallabag/wallabag/pull/1653) ([tcitworld](https://github.com/tcitworld)) +- User created via wallabag:install is now a SUPER\_ADMIN :crown: [\#1652](https://github.com/wallabag/wallabag/pull/1652) ([nicosomb](https://github.com/nicosomb)) +- Test if some keys are missing in v2 import [\#1651](https://github.com/wallabag/wallabag/pull/1651) ([nicosomb](https://github.com/nicosomb)) +- Exported entries were added twice in export file [\#1650](https://github.com/wallabag/wallabag/pull/1650) ([nicosomb](https://github.com/nicosomb)) +- Fix project version in installation command [\#1648](https://github.com/wallabag/wallabag/pull/1648) ([lologhi](https://github.com/lologhi)) +- Force prod env on project creation [\#1647](https://github.com/wallabag/wallabag/pull/1647) ([j0k3r](https://github.com/j0k3r)) +- Fix \#1643: fix the way to load the HTML template in 2factor auth [\#1645](https://github.com/wallabag/wallabag/pull/1645) ([nicosomb](https://github.com/nicosomb)) +- Fix \#1622: parameter for FosUser confirmation [\#1644](https://github.com/wallabag/wallabag/pull/1644) ([nicosomb](https://github.com/nicosomb)) +- Unescape content for Material card [\#1642](https://github.com/wallabag/wallabag/pull/1642) ([j0k3r](https://github.com/j0k3r)) +- Update import.rst to fix a doc typo [\#1641](https://github.com/wallabag/wallabag/pull/1641) ([nicosomb](https://github.com/nicosomb)) +- Fix \#1635: change database name for test environments \(mysql/pgsql\) [\#1639](https://github.com/wallabag/wallabag/pull/1639) ([nicosomb](https://github.com/nicosomb)) +- Exporttxt [\#1633](https://github.com/wallabag/wallabag/pull/1633) ([tcitworld](https://github.com/tcitworld)) +- fix \#1620 and introduce new parameters to export [\#1629](https://github.com/wallabag/wallabag/pull/1629) ([tcitworld](https://github.com/tcitworld)) +- \[\#1621\] Add default address for FosUser [\#1625](https://github.com/wallabag/wallabag/pull/1625) ([nicosomb](https://github.com/nicosomb)) +- Move public assets for themes [\#1619](https://github.com/wallabag/wallabag/pull/1619) ([j0k3r](https://github.com/j0k3r)) +- Updated version in README [\#1618](https://github.com/wallabag/wallabag/pull/1618) ([nicosomb](https://github.com/nicosomb)) +- Few fixes [\#1614](https://github.com/wallabag/wallabag/pull/1614) ([j0k3r](https://github.com/j0k3r)) +- Settings page [\#1612](https://github.com/wallabag/wallabag/pull/1612) ([j0k3r](https://github.com/j0k3r)) +- Disable xdebug all the time [\#1610](https://github.com/wallabag/wallabag/pull/1610) ([j0k3r](https://github.com/j0k3r)) +- Remove tag relation when removing an entry [\#1606](https://github.com/wallabag/wallabag/pull/1606) ([j0k3r](https://github.com/j0k3r)) +- \[\#1604\] First draft to fix SensioLabsInsight report [\#1605](https://github.com/wallabag/wallabag/pull/1605) ([nicosomb](https://github.com/nicosomb)) +- \[\#1590\] Add JSON import from wallabag v2 [\#1601](https://github.com/wallabag/wallabag/pull/1601) ([nicosomb](https://github.com/nicosomb)) +- Use translator interface instead of final class [\#1587](https://github.com/wallabag/wallabag/pull/1587) ([j0k3r](https://github.com/j0k3r)) +- some cleanup [\#1584](https://github.com/wallabag/wallabag/pull/1584) ([nicosomb](https://github.com/nicosomb)) +- Fix `findOneByUrl` side effect in tests [\#1583](https://github.com/wallabag/wallabag/pull/1583) ([j0k3r](https://github.com/j0k3r)) +- Fix typo and set version in the README [\#1574](https://github.com/wallabag/wallabag/pull/1574) ([jjanvier](https://github.com/jjanvier)) +- Use HTML email for 2FA [\#1565](https://github.com/wallabag/wallabag/pull/1565) ([j0k3r](https://github.com/j0k3r)) +- Remove Flattr references [\#1564](https://github.com/wallabag/wallabag/pull/1564) ([nicosomb](https://github.com/nicosomb)) +- \#1394 Fixed 'mark as read' and 'mark as unread' links [\#1563](https://github.com/wallabag/wallabag/pull/1563) ([nicosomb](https://github.com/nicosomb)) +- Fix \#1501 Quickstart for beginners [\#1560](https://github.com/wallabag/wallabag/pull/1560) ([nicosomb](https://github.com/nicosomb)) +- Installation & configuration of Doctrine Migrations [\#1559](https://github.com/wallabag/wallabag/pull/1559) ([nicosomb](https://github.com/nicosomb)) +- Upgrade to Symfony 3.0 [\#1524](https://github.com/wallabag/wallabag/pull/1524) ([j0k3r](https://github.com/j0k3r)) -- [#2016](https://github.com/wallabag/wallabag/pull/2016) Big updates in [our documentation](http://doc.wallabag.org/en/master/) (Nicolas Lœuillet) -- [#2028](https://github.com/wallabag/wallabag/pull/2028) Documentation about android application (Strubbl) -- [#2019](https://github.com/wallabag/wallabag/pull/2019) Italian translation (Daniele Conca) -- [#2011](https://github.com/wallabag/wallabag/pull/2011) Documentation about wallabag upgrade (biva) -- [#1985](https://github.com/wallabag/wallabag/pull/1985) Documentation about rights access (FoxMaSk) -- [#1969](https://github.com/wallabag/wallabag/pull/1969) Third resources for API in documentation (Nicolas Lœuillet) -- [#1967](https://github.com/wallabag/wallabag/pull/1967) FAQ page in documentation (Nicolas Lœuillet) +## [2.0.0-alpha.2](https://github.com/wallabag/wallabag/tree/2.0.0-alpha.2) (2016-01-22) +**Merged pull requests:** -### Changed +- Ignore composer.lock [\#1607](https://github.com/wallabag/wallabag/pull/1607) ([j0k3r](https://github.com/j0k3r)) +- Add configuration for portuguese documentation [\#1599](https://github.com/wallabag/wallabag/pull/1599) ([nicosomb](https://github.com/nicosomb)) +- Create index.rst [\#1594](https://github.com/wallabag/wallabag/pull/1594) ([araujo0205](https://github.com/araujo0205)) +- \#1490 HTML in E-Mails [\#1592](https://github.com/wallabag/wallabag/pull/1592) ([modos189](https://github.com/modos189)) +- Add caffereggio.net [\#1588](https://github.com/wallabag/wallabag/pull/1588) ([benages](https://github.com/benages)) +- Fix grammatical typo [\#1582](https://github.com/wallabag/wallabag/pull/1582) ([simark](https://github.com/simark)) +- add import link on baggy theme [\#1562](https://github.com/wallabag/wallabag/pull/1562) ([nicosomb](https://github.com/nicosomb)) +- Add basic docker-compose configuration [\#1561](https://github.com/wallabag/wallabag/pull/1561) ([FabienM](https://github.com/FabienM)) +- \[v2\] documentation cleaning [\#1558](https://github.com/wallabag/wallabag/pull/1558) ([nicosomb](https://github.com/nicosomb)) -- [#1977](https://github.com/wallabag/wallabag/pull/1977) Spanish documentation (jami7) +## [2.0.0-alpha.1](https://github.com/wallabag/wallabag/tree/2.0.0-alpha.1) (2016-01-07) +**Merged pull requests:** -### Fixed +- Updated composer.lock [\#1537](https://github.com/wallabag/wallabag/pull/1537) ([bdunogier](https://github.com/bdunogier)) +- Add popehat.com [\#1535](https://github.com/wallabag/wallabag/pull/1535) ([benages](https://github.com/benages)) +- Modified blog.cloudflare.com [\#1530](https://github.com/wallabag/wallabag/pull/1530) ([benages](https://github.com/benages)) +- Add letraslibres and lawfareblog [\#1526](https://github.com/wallabag/wallabag/pull/1526) ([benages](https://github.com/benages)) +- SMTP from environment variables [\#1518](https://github.com/wallabag/wallabag/pull/1518) ([mathbruyen](https://github.com/mathbruyen)) +- add docs [\#1495](https://github.com/wallabag/wallabag/pull/1495) ([nicosomb](https://github.com/nicosomb)) +- v2 – 1st draft for Pocket import via API & Wallabag v1 import [\#1493](https://github.com/wallabag/wallabag/pull/1493) ([nicosomb](https://github.com/nicosomb)) +- Correction to naiz.eus [\#1489](https://github.com/wallabag/wallabag/pull/1489) ([benages](https://github.com/benages)) +- Corrections to chronicle.com [\#1486](https://github.com/wallabag/wallabag/pull/1486) ([benages](https://github.com/benages)) +- fix Content-Disposition header \#1481 [\#1483](https://github.com/wallabag/wallabag/pull/1483) ([alkahan](https://github.com/alkahan)) +- V2 material [\#1475](https://github.com/wallabag/wallabag/pull/1475) ([modos189](https://github.com/modos189)) +- Add three new sites [\#1466](https://github.com/wallabag/wallabag/pull/1466) ([benages](https://github.com/benages)) +- Add aclu.org [\#1460](https://github.com/wallabag/wallabag/pull/1460) ([benages](https://github.com/benages)) +- fix \#1457 & update materialize [\#1459](https://github.com/wallabag/wallabag/pull/1459) ([tcitworld](https://github.com/tcitworld)) +- Add diagonalperiodico.net [\#1455](https://github.com/wallabag/wallabag/pull/1455) ([benages](https://github.com/benages)) +- Add wired.de site config [\#1451](https://github.com/wallabag/wallabag/pull/1451) ([benages](https://github.com/benages)) +- Add support to timeshighereducation.com [\#1450](https://github.com/wallabag/wallabag/pull/1450) ([benages](https://github.com/benages)) +- typo [\#1447](https://github.com/wallabag/wallabag/pull/1447) ([tcitworld](https://github.com/tcitworld)) +- Hide export button if we are not on entries list [\#1553](https://github.com/wallabag/wallabag/pull/1553) ([nicosomb](https://github.com/nicosomb)) +- v2 – Fix wrong if when deleting an entry [\#1547](https://github.com/wallabag/wallabag/pull/1547) ([j0k3r](https://github.com/j0k3r)) +- v2 – Ability to reload an entry [\#1546](https://github.com/wallabag/wallabag/pull/1546) ([j0k3r](https://github.com/j0k3r)) +- v2 – Remove user reference in tag [\#1545](https://github.com/wallabag/wallabag/pull/1545) ([j0k3r](https://github.com/j0k3r)) +- v2 – Add custom email for 2FA [\#1544](https://github.com/wallabag/wallabag/pull/1544) ([j0k3r](https://github.com/j0k3r)) +- v2 – Avoid multiple tag creation [\#1542](https://github.com/wallabag/wallabag/pull/1542) ([j0k3r](https://github.com/j0k3r)) +- v2 – Don't redirect to the content page after deletion [\#1540](https://github.com/wallabag/wallabag/pull/1540) ([j0k3r](https://github.com/j0k3r)) +- changed table name for tagging rule [\#1529](https://github.com/wallabag/wallabag/pull/1529) ([nicosomb](https://github.com/nicosomb)) +- Typo [\#1523](https://github.com/wallabag/wallabag/pull/1523) ([tcitworld](https://github.com/tcitworld)) +- Add graby log [\#1505](https://github.com/wallabag/wallabag/pull/1505) ([j0k3r](https://github.com/j0k3r)) +- fix \#1005 V2 redirect after deletion [\#1496](https://github.com/wallabag/wallabag/pull/1496) ([nicosomb](https://github.com/nicosomb)) +- Add german translation. [\#1491](https://github.com/wallabag/wallabag/pull/1491) ([jlnostr](https://github.com/jlnostr)) +- Slug for tags [\#1488](https://github.com/wallabag/wallabag/pull/1488) ([nicosomb](https://github.com/nicosomb)) +- 2factor authentication via email [\#1484](https://github.com/wallabag/wallabag/pull/1484) ([nicosomb](https://github.com/nicosomb)) +- fix \#1470: clean material theme [\#1472](https://github.com/wallabag/wallabag/pull/1472) ([nicosomb](https://github.com/nicosomb)) +- implement bookmarklet [\#1471](https://github.com/wallabag/wallabag/pull/1471) ([nicosomb](https://github.com/nicosomb)) +- fix bug on detect active theme [\#1468](https://github.com/wallabag/wallabag/pull/1468) ([nicosomb](https://github.com/nicosomb)) +- fix type hint for User [\#1464](https://github.com/wallabag/wallabag/pull/1464) ([nicosomb](https://github.com/nicosomb)) +- fix \#1433: add test for removeWww Twig Extension [\#1461](https://github.com/wallabag/wallabag/pull/1461) ([nicosomb](https://github.com/nicosomb)) +- Add french translation [\#1441](https://github.com/wallabag/wallabag/pull/1441) ([nicosomb](https://github.com/nicosomb)) +- fix \#1433: remove www. on entries view [\#1440](https://github.com/wallabag/wallabag/pull/1440) ([nicosomb](https://github.com/nicosomb)) +- fix Capistrano configuration [\#1439](https://github.com/wallabag/wallabag/pull/1439) ([nicosomb](https://github.com/nicosomb)) +- some cleanup on material theme [\#1437](https://github.com/wallabag/wallabag/pull/1437) ([nicosomb](https://github.com/nicosomb)) +- Public registration & oAuth2 \o/ [\#1436](https://github.com/wallabag/wallabag/pull/1436) ([nicosomb](https://github.com/nicosomb)) +- Enabled created user from Config [\#1435](https://github.com/wallabag/wallabag/pull/1435) ([j0k3r](https://github.com/j0k3r)) +- V2 – Handle language in entry [\#1429](https://github.com/wallabag/wallabag/pull/1429) ([j0k3r](https://github.com/j0k3r)) +- V2 – Export entries [\#1422](https://github.com/wallabag/wallabag/pull/1422) ([tcitworld](https://github.com/tcitworld)) -- [#2023](https://github.com/wallabag/wallabag/pull/2023) Fix translation for validators (Nicolas Lœuillet) -- [#2020](https://github.com/wallabag/wallabag/pull/2020) Fix number of entries in tag/list (Nicolas Lœuillet) -- [#2022](https://github.com/wallabag/wallabag/pull/2022) Fix pagination bar on small devices (Nicolas Lœuillet) -- [#2013](https://github.com/wallabag/wallabag/pull/2013) Fix tag listing (Nicolas Lœuillet) -- [#1976](https://github.com/wallabag/wallabag/pull/1976) Fix filter reading time (Nicolas Lœuillet) -- [#2005](https://github.com/wallabag/wallabag/pull/2005) Fix reading speed not defined when user was created via config page (Nicolas Lœuillet) -- [#2010](https://github.com/wallabag/wallabag/pull/2010) Set the title via POST /api/entries (Nicolas Lœuillet) +## [2.0.0-alpha.0](https://github.com/wallabag/wallabag/tree/2.0.0-alpha.0) (2015-09-14) +**Merged pull requests:** -## [2.0.3] - 2016-04-22 +- improved display pictures [\#1420](https://github.com/wallabag/wallabag/pull/1420) ([modos189](https://github.com/modos189)) +- Update CONTRIBUTING.md [\#1415](https://github.com/wallabag/wallabag/pull/1415) ([sridharrajs](https://github.com/sridharrajs)) +- Use full size of the panel for domain-filtering [\#1403](https://github.com/wallabag/wallabag/pull/1403) ([tcitworld](https://github.com/tcitworld)) +- Enable code rating on Scrutinizer [\#1390](https://github.com/wallabag/wallabag/pull/1390) ([j0k3r](https://github.com/j0k3r)) +- change timeout because scrutinizer failed [\#1358](https://github.com/wallabag/wallabag/pull/1358) ([nicosomb](https://github.com/nicosomb)) +- fixes material [\#1337](https://github.com/wallabag/wallabag/pull/1337) ([modos189](https://github.com/modos189)) +- Update Useragent to Win8.1 and FF36 [\#1318](https://github.com/wallabag/wallabag/pull/1318) ([ticoombs](https://github.com/ticoombs)) +- Fix ff social [\#1301](https://github.com/wallabag/wallabag/pull/1301) ([tcitworld](https://github.com/tcitworld)) +- fix material [\#1298](https://github.com/wallabag/wallabag/pull/1298) ([modos189](https://github.com/modos189)) +- 1.9.1-b [\#1274](https://github.com/wallabag/wallabag/pull/1274) ([nicosomb](https://github.com/nicosomb)) +- remove new syntax for array [\#1272](https://github.com/wallabag/wallabag/pull/1272) ([nicosomb](https://github.com/nicosomb)) +- 1.9.1 [\#1267](https://github.com/wallabag/wallabag/pull/1267) ([nicosomb](https://github.com/nicosomb)) +- Fix site config [\#1266](https://github.com/wallabag/wallabag/pull/1266) ([nicosomb](https://github.com/nicosomb)) +- Dev autoclose [\#1257](https://github.com/wallabag/wallabag/pull/1257) ([freddyb](https://github.com/freddyb)) +- fix security bug with PHP\_AUTH\_USER [\#1252](https://github.com/wallabag/wallabag/pull/1252) ([tcitworld](https://github.com/tcitworld)) +- fix link to documentation [\#1251](https://github.com/wallabag/wallabag/pull/1251) ([axelsimon](https://github.com/axelsimon)) +- Revert "fix link to save your first article" [\#1248](https://github.com/wallabag/wallabag/pull/1248) ([tcitworld](https://github.com/tcitworld)) +- fix link to save your first article [\#1247](https://github.com/wallabag/wallabag/pull/1247) ([axelsimon](https://github.com/axelsimon)) +- add site config for des-livres-pour-changer-de-vie.fr [\#1234](https://github.com/wallabag/wallabag/pull/1234) ([jmlrt](https://github.com/jmlrt)) +- add site\_config for cloudacademy.com and cyrille-borne.com [\#1228](https://github.com/wallabag/wallabag/pull/1228) ([jmlrt](https://github.com/jmlrt)) +- Enable base\_url for proxies [\#1226](https://github.com/wallabag/wallabag/pull/1226) ([tcitworld](https://github.com/tcitworld)) +- Fix \#1215 [\#1217](https://github.com/wallabag/wallabag/pull/1217) ([tcitworld](https://github.com/tcitworld)) +- Update fr\_FR.utf8.po [\#1211](https://github.com/wallabag/wallabag/pull/1211) ([goofy-bz](https://github.com/goofy-bz)) +- issue \#1203: fixing tag export [\#1207](https://github.com/wallabag/wallabag/pull/1207) ([vpmalley](https://github.com/vpmalley)) +- fix restart of import [\#1200](https://github.com/wallabag/wallabag/pull/1200) ([nicosomb](https://github.com/nicosomb)) +- double closing parentheses [\#1158](https://github.com/wallabag/wallabag/pull/1158) ([bohni](https://github.com/bohni)) +- composer: min PHP 5.3 added [\#1151](https://github.com/wallabag/wallabag/pull/1151) ([TomasVotruba](https://github.com/TomasVotruba)) +- Wallabag v2 api hypermedia pager [\#1148](https://github.com/wallabag/wallabag/pull/1148) ([willdurand](https://github.com/willdurand)) +- locale es\_ES.utf8 files fully translated → Translation to Spanish updated [\#1106](https://github.com/wallabag/wallabag/pull/1106) ([icaroperseo](https://github.com/icaroperseo)) +- Version 1.9.0 [\#1081](https://github.com/wallabag/wallabag/pull/1081) ([tcitworld](https://github.com/tcitworld)) +- added site config for github gists, jungle-world.com and toolinux.com [\#1067](https://github.com/wallabag/wallabag/pull/1067) ([vpmalley](https://github.com/vpmalley)) +- V2 api tests entry [\#1066](https://github.com/wallabag/wallabag/pull/1066) ([j0k3r](https://github.com/j0k3r)) +- Cleanup default assets symlinks [\#1065](https://github.com/wallabag/wallabag/pull/1065) ([FabienM](https://github.com/FabienM)) +- add a real relation between user and entry [\#1062](https://github.com/wallabag/wallabag/pull/1062) ([nicosomb](https://github.com/nicosomb)) +- Remove temporary entities [\#1061](https://github.com/wallabag/wallabag/pull/1061) ([j0k3r](https://github.com/j0k3r)) +- rename User entity [\#1060](https://github.com/wallabag/wallabag/pull/1060) ([nicosomb](https://github.com/nicosomb)) +- Rename entry [\#1059](https://github.com/wallabag/wallabag/pull/1059) ([nicosomb](https://github.com/nicosomb)) +- PATCH method, boolean for some parameters and change entity methods name [\#1055](https://github.com/wallabag/wallabag/pull/1055) ([nicosomb](https://github.com/nicosomb)) +- we can now set a title to the article when we use POST /api/entries [\#1054](https://github.com/wallabag/wallabag/pull/1054) ([nicosomb](https://github.com/nicosomb)) +- Fix fetched entries when localized [\#1053](https://github.com/wallabag/wallabag/pull/1053) ([erixtekila](https://github.com/erixtekila)) +- Added en-GB [\#1042](https://github.com/wallabag/wallabag/pull/1042) ([edent](https://github.com/edent)) +- CS [\#1037](https://github.com/wallabag/wallabag/pull/1037) ([j0k3r](https://github.com/j0k3r)) +- Update composer [\#1036](https://github.com/wallabag/wallabag/pull/1036) ([j0k3r](https://github.com/j0k3r)) +- Tests are working again [\#1035](https://github.com/wallabag/wallabag/pull/1035) ([j0k3r](https://github.com/j0k3r)) +- Update german localisation. [\#1031](https://github.com/wallabag/wallabag/pull/1031) ([jlnostr](https://github.com/jlnostr)) +- Revert "Update german localisation." [\#1030](https://github.com/wallabag/wallabag/pull/1030) ([tcitworld](https://github.com/tcitworld)) +- Update german localisation. [\#1028](https://github.com/wallabag/wallabag/pull/1028) ([jlnostr](https://github.com/jlnostr)) +- New installation process [\#1026](https://github.com/wallabag/wallabag/pull/1026) ([tcitworld](https://github.com/tcitworld)) +- Send email at registration [\#1022](https://github.com/wallabag/wallabag/pull/1022) ([tcitworld](https://github.com/tcitworld)) +- working on registration support [\#1021](https://github.com/wallabag/wallabag/pull/1021) ([tcitworld](https://github.com/tcitworld)) +- Revert "URL encode 'via @wallabagapp' email body" [\#1019](https://github.com/wallabag/wallabag/pull/1019) ([nicosomb](https://github.com/nicosomb)) +- URL encode 'via @wallabagapp' email body [\#1018](https://github.com/wallabag/wallabag/pull/1018) ([shtrom](https://github.com/shtrom)) +- Savewithtags [\#1015](https://github.com/wallabag/wallabag/pull/1015) ([tcitworld](https://github.com/tcitworld)) +- Importtags [\#1013](https://github.com/wallabag/wallabag/pull/1013) ([tcitworld](https://github.com/tcitworld)) +- Fixed \#993: add wallabag to packagist [\#1004](https://github.com/wallabag/wallabag/pull/1004) ([nicosomb](https://github.com/nicosomb)) +- \[RSS\] introducing query param 'limit' for max items in RSS feed [\#1003](https://github.com/wallabag/wallabag/pull/1003) ([vpmalley](https://github.com/vpmalley)) +- Miscellaneous additions [\#999](https://github.com/wallabag/wallabag/pull/999) ([moparisthebest](https://github.com/moparisthebest)) +- Update fr\_FR.utf8.po [\#986](https://github.com/wallabag/wallabag/pull/986) ([goofy-bz](https://github.com/goofy-bz)) +- don't call flattr if flattr is disabled [\#975](https://github.com/wallabag/wallabag/pull/975) ([tcitworld](https://github.com/tcitworld)) +- Make call to FTRSS silent to avoid warnings which can break import [\#974](https://github.com/wallabag/wallabag/pull/974) ([jbfavre](https://github.com/jbfavre)) +- Fix jquery installation [\#971](https://github.com/wallabag/wallabag/pull/971) ([nicosomb](https://github.com/nicosomb)) +- added reload function [\#968](https://github.com/wallabag/wallabag/pull/968) ([tcitworld](https://github.com/tcitworld)) +- implemented random button [\#964](https://github.com/wallabag/wallabag/pull/964) ([tcitworld](https://github.com/tcitworld)) +- Update wallabag\_compatibility\_test.php [\#958](https://github.com/wallabag/wallabag/pull/958) ([etiess](https://github.com/etiess)) +- remove courgette theme [\#953](https://github.com/wallabag/wallabag/pull/953) ([nicosomb](https://github.com/nicosomb)) +- Fixing icon text on unread page [\#945](https://github.com/wallabag/wallabag/pull/945) ([jsit](https://github.com/jsit)) +- Globalizing and creating two variants of Carrot.org icon [\#944](https://github.com/wallabag/wallabag/pull/944) ([jsit](https://github.com/jsit)) +- fix Flattrs [\#943](https://github.com/wallabag/wallabag/pull/943) ([tcitworld](https://github.com/tcitworld)) +- implementing carrot into wallabag [\#942](https://github.com/wallabag/wallabag/pull/942) ([tcitworld](https://github.com/tcitworld)) +- \_global-izing some more js & css [\#939](https://github.com/wallabag/wallabag/pull/939) ([jsit](https://github.com/jsit)) +- Fix for favicons in Firefox [\#938](https://github.com/wallabag/wallabag/pull/938) ([jsit](https://github.com/jsit)) +- Moving favicons to new \_global theme dir and adjusting \ tags [\#937](https://github.com/wallabag/wallabag/pull/937) ([jsit](https://github.com/jsit)) +- 1.8.1b [\#933](https://github.com/wallabag/wallabag/pull/933) ([tcitworld](https://github.com/tcitworld)) +- 1.8.1 [\#932](https://github.com/wallabag/wallabag/pull/932) ([nicosomb](https://github.com/nicosomb)) +- Fixing URL typo on about page [\#926](https://github.com/wallabag/wallabag/pull/926) ([jsit](https://github.com/jsit)) +- add HTML documentation [\#923](https://github.com/wallabag/wallabag/pull/923) ([nicosomb](https://github.com/nicosomb)) +- Fixing menu hiding behavior when switching from mobile width to desktop width [\#922](https://github.com/wallabag/wallabag/pull/922) ([jsit](https://github.com/jsit)) +- add about page [\#921](https://github.com/wallabag/wallabag/pull/921) ([nicosomb](https://github.com/nicosomb)) +- Many small UI changes/fixes to Baggy theme and English translation files [\#920](https://github.com/wallabag/wallabag/pull/920) ([jsit](https://github.com/jsit)) +- Improving JavaScript for popup forms [\#919](https://github.com/wallabag/wallabag/pull/919) ([jsit](https://github.com/jsit)) +- Fixing opacity issue when using mobile menu \(\#912\) [\#917](https://github.com/wallabag/wallabag/pull/917) ([jsit](https://github.com/jsit)) +- Fixing regression in popup close button styling [\#913](https://github.com/wallabag/wallabag/pull/913) ([jsit](https://github.com/jsit)) +- Standardizing style and code for menu popup forms [\#911](https://github.com/wallabag/wallabag/pull/911) ([jsit](https://github.com/jsit)) +- Moving search-form style out of messages css and into style.css [\#910](https://github.com/wallabag/wallabag/pull/910) ([jsit](https://github.com/jsit)) +- Standardizing class names and styles for close buttons [\#909](https://github.com/wallabag/wallabag/pull/909) ([jsit](https://github.com/jsit)) +- Changing close message button to use × instead of X [\#908](https://github.com/wallabag/wallabag/pull/908) ([jsit](https://github.com/jsit)) +- Some small design tweaks to the login page: Moving 'Stay signed in'... [\#907](https://github.com/wallabag/wallabag/pull/907) ([jsit](https://github.com/jsit)) +- Uppercasing 'tags' page title [\#906](https://github.com/wallabag/wallabag/pull/906) ([jsit](https://github.com/jsit)) +- Making the close button more visually consistent on the menu popup forms [\#905](https://github.com/wallabag/wallabag/pull/905) ([jsit](https://github.com/jsit)) +- Vagrantfile [\#904](https://github.com/wallabag/wallabag/pull/904) ([nicosomb](https://github.com/nicosomb)) +- Fixing some more text case issues [\#903](https://github.com/wallabag/wallabag/pull/903) ([jsit](https://github.com/jsit)) +- Adding 'en\_US' locale \(issue \#901\) [\#902](https://github.com/wallabag/wallabag/pull/902) ([jsit](https://github.com/jsit)) +- Fixing a bunch of English translation letter casing and syntax \(issue \#899\) [\#900](https://github.com/wallabag/wallabag/pull/900) ([jsit](https://github.com/jsit)) +- Fixing issue \#874, displaying preview text when in list mode [\#898](https://github.com/wallabag/wallabag/pull/898) ([jsit](https://github.com/jsit)) +- Fixing display mode switching in Baggy theme \(issue \#896\) [\#897](https://github.com/wallabag/wallabag/pull/897) ([jsit](https://github.com/jsit)) +- Fix \#891: change type for password field in installation [\#894](https://github.com/wallabag/wallabag/pull/894) ([nicosomb](https://github.com/nicosomb)) +- Fix\#871 [\#889](https://github.com/wallabag/wallabag/pull/889) ([tcitworld](https://github.com/tcitworld)) +- updated site\_config [\#888](https://github.com/wallabag/wallabag/pull/888) ([nicosomb](https://github.com/nicosomb)) +- fix \#882 [\#883](https://github.com/wallabag/wallabag/pull/883) ([tcitworld](https://github.com/tcitworld)) +- update zeit.de.txt for removal of inline ads [\#879](https://github.com/wallabag/wallabag/pull/879) ([Marmo](https://github.com/Marmo)) +- fix \#873 [\#878](https://github.com/wallabag/wallabag/pull/878) ([tcitworld](https://github.com/tcitworld)) +- fix for \#830 [\#868](https://github.com/wallabag/wallabag/pull/868) ([tcitworld](https://github.com/tcitworld)) +- fix z-index-menu mobile view bug \#834 [\#867](https://github.com/wallabag/wallabag/pull/867) ([tcitworld](https://github.com/tcitworld)) +- update heise.de.txt [\#865](https://github.com/wallabag/wallabag/pull/865) ([Marmo](https://github.com/Marmo)) +- 1.8.0 [\#863](https://github.com/wallabag/wallabag/pull/863) ([nicosomb](https://github.com/nicosomb)) +- query for populate mysql/postgres was called when we choosed sqlite [\#861](https://github.com/wallabag/wallabag/pull/861) ([nicosomb](https://github.com/nicosomb)) +- Move compatibility file \(fixes \#858\) [\#860](https://github.com/wallabag/wallabag/pull/860) ([nicosomb](https://github.com/nicosomb)) +- Fix \#826 [\#856](https://github.com/wallabag/wallabag/pull/856) ([tcitworld](https://github.com/tcitworld)) +- Fix \#828 [\#855](https://github.com/wallabag/wallabag/pull/855) ([tcitworld](https://github.com/tcitworld)) +- Saveclick2search \(fix for \#831\) [\#854](https://github.com/wallabag/wallabag/pull/854) ([tcitworld](https://github.com/tcitworld)) +- Fix for \#797 [\#853](https://github.com/wallabag/wallabag/pull/853) ([tcitworld](https://github.com/tcitworld)) +- Refactor [\#851](https://github.com/wallabag/wallabag/pull/851) ([tcitworld](https://github.com/tcitworld)) +- Add support for custom http port [\#848](https://github.com/wallabag/wallabag/pull/848) ([11mariom](https://github.com/11mariom)) +- Fix the PostgreSQL install errors [\#845](https://github.com/wallabag/wallabag/pull/845) ([rros](https://github.com/rros)) +- Convert the MySQL charset to utf8mb4 to support the full range of unicode [\#843](https://github.com/wallabag/wallabag/pull/843) ([rros](https://github.com/rros)) +- Fix \#766 - GD detection [\#841](https://github.com/wallabag/wallabag/pull/841) ([tcitworld](https://github.com/tcitworld)) +- fix pictures display when DOWNLOAD\_PICTURES is enabled [\#839](https://github.com/wallabag/wallabag/pull/839) ([tcitworld](https://github.com/tcitworld)) +- Implemented additional check for using the 'X-Forwarded-Port' header. [\#836](https://github.com/wallabag/wallabag/pull/836) ([akoenig](https://github.com/akoenig)) +- Fix downloading SQLite database from all users [\#819](https://github.com/wallabag/wallabag/pull/819) ([tcitworld](https://github.com/tcitworld)) +- Complete rework of faz.net-template [\#816](https://github.com/wallabag/wallabag/pull/816) ([zinnober](https://github.com/zinnober)) +- vendor dir is not accessible before install, sqlite db dir write check moved into db class [\#814](https://github.com/wallabag/wallabag/pull/814) ([mariroz](https://github.com/mariroz)) +- Adduseremail [\#808](https://github.com/wallabag/wallabag/pull/808) ([tcitworld](https://github.com/tcitworld)) +- Traductionfix [\#802](https://github.com/wallabag/wallabag/pull/802) ([tcitworld](https://github.com/tcitworld)) +- Ebook [\#801](https://github.com/wallabag/wallabag/pull/801) ([tcitworld](https://github.com/tcitworld)) +- security fix [\#791](https://github.com/wallabag/wallabag/pull/791) ([mariroz](https://github.com/mariroz)) +- security issue [\#790](https://github.com/wallabag/wallabag/pull/790) ([mariroz](https://github.com/mariroz)) +- config for habrahabr.ru to grab articles with comments [\#789](https://github.com/wallabag/wallabag/pull/789) ([mariroz](https://github.com/mariroz)) +- Add data for mysql installation, see \#624 [\#787](https://github.com/wallabag/wallabag/pull/787) ([nicosomb](https://github.com/nicosomb)) +- highlight.js library added to highlight programming code examples in article view [\#786](https://github.com/wallabag/wallabag/pull/786) ([mariroz](https://github.com/mariroz)) +- change default pagination, set it to 12, to have a nice baggy display [\#785](https://github.com/wallabag/wallabag/pull/785) ([nicosomb](https://github.com/nicosomb)) +- fix display of 'Done' message when we add a link from 'save a link' item [\#784](https://github.com/wallabag/wallabag/pull/784) ([nicosomb](https://github.com/nicosomb)) +- \#763 fix to display the login successful message with the translation [\#783](https://github.com/wallabag/wallabag/pull/783) ([nicosomb](https://github.com/nicosomb)) +- Fix \#776 [\#782](https://github.com/wallabag/wallabag/pull/782) ([nicosomb](https://github.com/nicosomb)) +- Proposed fix for \#773 [\#780](https://github.com/wallabag/wallabag/pull/780) ([anno1337](https://github.com/anno1337)) +- Updated polish translation [\#777](https://github.com/wallabag/wallabag/pull/777) ([skibbipl](https://github.com/skibbipl)) +- issue \#750 - config for dn.pt site added [\#775](https://github.com/wallabag/wallabag/pull/775) ([mariroz](https://github.com/mariroz)) +- fixed bug for epub export \#755 ; also better metadata title [\#771](https://github.com/wallabag/wallabag/pull/771) ([tcitworld](https://github.com/tcitworld)) +- Refactor [\#768](https://github.com/wallabag/wallabag/pull/768) ([nicosomb](https://github.com/nicosomb)) +- Refactor [\#764](https://github.com/wallabag/wallabag/pull/764) ([tcitworld](https://github.com/tcitworld)) +- 1.7.1 [\#761](https://github.com/wallabag/wallabag/pull/761) ([nicosomb](https://github.com/nicosomb)) +- Add support for \*.about.com [\#754](https://github.com/wallabag/wallabag/pull/754) ([sinisterstuf](https://github.com/sinisterstuf)) +- fix of issue \#650, \#619 and other similar, error in JSLikeHTMLElement: node no longer exists. [\#752](https://github.com/wallabag/wallabag/pull/752) ([mariroz](https://github.com/mariroz)) +- quick fix of issue \#750: mulipage content for politico.com/magazine articles [\#751](https://github.com/wallabag/wallabag/pull/751) ([mariroz](https://github.com/mariroz)) +- fix of issue \#677: When downloading images, wallabag doesnt respect html "base" tag, tnx to @fivefilters [\#737](https://github.com/wallabag/wallabag/pull/737) ([mariroz](https://github.com/mariroz)) +- fix of issue \#718: Error parsing file imported from Pocket \#718 [\#736](https://github.com/wallabag/wallabag/pull/736) ([mariroz](https://github.com/mariroz)) +- typo FR après vérif. [\#728](https://github.com/wallabag/wallabag/pull/728) ([Draky50110](https://github.com/Draky50110)) +- typo FR [\#724](https://github.com/wallabag/wallabag/pull/724) ([Draky50110](https://github.com/Draky50110)) +- do not output debug while generating epub [\#722](https://github.com/wallabag/wallabag/pull/722) ([tcitworld](https://github.com/tcitworld)) +- error reporting level set in E\_ALL & ~E\_NOTICE by default, can be overriden in config [\#716](https://github.com/wallabag/wallabag/pull/716) ([mariroz](https://github.com/mariroz)) +- fix of undefined ATOM constant warning in full-text-rss, will fix ios-app issue \#14 [\#715](https://github.com/wallabag/wallabag/pull/715) ([mariroz](https://github.com/mariroz)) +- small xss vulnerability and translation ability fix [\#713](https://github.com/wallabag/wallabag/pull/713) ([mariroz](https://github.com/mariroz)) +- 1.7, call me "Premium version" [\#712](https://github.com/wallabag/wallabag/pull/712) ([nicosomb](https://github.com/nicosomb)) +- fix of uninitialized object warning, issue \#710 [\#711](https://github.com/wallabag/wallabag/pull/711) ([mariroz](https://github.com/mariroz)) +- update to 3.2 version of full-text-rss, issue \#694 [\#707](https://github.com/wallabag/wallabag/pull/707) ([mariroz](https://github.com/mariroz)) +- Fixed Baggy theme CSS for Chrome Extension \(and \< 500px width layout\) [\#706](https://github.com/wallabag/wallabag/pull/706) ([gboudreau](https://github.com/gboudreau)) +- \[V2\] changed everything from Poche to Wallabag [\#705](https://github.com/wallabag/wallabag/pull/705) ([tcitworld](https://github.com/tcitworld)) +- Security fixes for downloaded images \(thanks @leblanc-simon\) [\#703](https://github.com/wallabag/wallabag/pull/703) ([tcitworld](https://github.com/tcitworld)) +- Up-to-date with me [\#697](https://github.com/wallabag/wallabag/pull/697) ([tcitworld](https://github.com/tcitworld)) +- better wallabag in wallabag button \(see \#685\) [\#693](https://github.com/wallabag/wallabag/pull/693) ([tcitworld](https://github.com/tcitworld)) +- Epub is here ! [\#691](https://github.com/wallabag/wallabag/pull/691) ([tcitworld](https://github.com/tcitworld)) +- Add message in web server log in case of authentication failure [\#690](https://github.com/wallabag/wallabag/pull/690) ([doc75](https://github.com/doc75)) +- Tag-related features [\#689](https://github.com/wallabag/wallabag/pull/689) ([tcitworld](https://github.com/tcitworld)) +- French translation update [\#680](https://github.com/wallabag/wallabag/pull/680) ([m-r-r](https://github.com/m-r-r)) +- fix of putenv warning under safe mode, issue \#646 [\#667](https://github.com/wallabag/wallabag/pull/667) ([mariroz](https://github.com/mariroz)) +- fix of rss headers problem [\#665](https://github.com/wallabag/wallabag/pull/665) ([mariroz](https://github.com/mariroz)) +- Multi-user system \(end\) [\#663](https://github.com/wallabag/wallabag/pull/663) ([tcitworld](https://github.com/tcitworld)) +- last error: config.inc.default.php missed [\#659](https://github.com/wallabag/wallabag/pull/659) ([mariroz](https://github.com/mariroz)) +- inc/poche/config.inc.php.new removed [\#658](https://github.com/wallabag/wallabag/pull/658) ([mariroz](https://github.com/mariroz)) +- parse mobile.lemondeinformatique.fr [\#655](https://github.com/wallabag/wallabag/pull/655) ([ghost](https://github.com/ghost)) +- print view fixed in baggy; print link added; read percent added in default theme; archive and favorite re-factored to be ajax action in article view [\#653](https://github.com/wallabag/wallabag/pull/653) ([mariroz](https://github.com/mariroz)) +- \[fix\] rss feed content type set to text/xml \#636 [\#643](https://github.com/wallabag/wallabag/pull/643) ([nicosomb](https://github.com/nicosomb)) +- Added IF NOT EXISTS [\#642](https://github.com/wallabag/wallabag/pull/642) ([jplock](https://github.com/jplock)) +- Improvements on french translation [\#637](https://github.com/wallabag/wallabag/pull/637) ([AmauryCarrade](https://github.com/AmauryCarrade)) +- added missing image to dmagenta theme [\#635](https://github.com/wallabag/wallabag/pull/635) ([DmitrySandalov](https://github.com/DmitrySandalov)) +- 1.6.1 [\#634](https://github.com/wallabag/wallabag/pull/634) ([nicosomb](https://github.com/nicosomb)) +- error with empty content by import fixed. Also youtube and vimeo videos are allowd in content now. [\#633](https://github.com/wallabag/wallabag/pull/633) ([mariroz](https://github.com/mariroz)) +- logging by import added; error by creation table if exists in postgresql... [\#632](https://github.com/wallabag/wallabag/pull/632) ([mariroz](https://github.com/mariroz)) +- Fix display problem with smartphone and Baggy \#610 [\#623](https://github.com/wallabag/wallabag/pull/623) ([nicosomb](https://github.com/nicosomb)) +- \[fix\] Escap key displays save a link form \#612 [\#622](https://github.com/wallabag/wallabag/pull/622) ([nicosomb](https://github.com/nicosomb)) +- can't close search popup with the cross picture \#613 [\#621](https://github.com/wallabag/wallabag/pull/621) ([nicosomb](https://github.com/nicosomb)) +- remove duplicates by import; code formatting changes: tabs replaced with spaces [\#618](https://github.com/wallabag/wallabag/pull/618) ([mariroz](https://github.com/mariroz)) +- \[fix\] with some config, http host is different [\#614](https://github.com/wallabag/wallabag/pull/614) ([nicosomb](https://github.com/nicosomb)) +- update french locale, thx to @goofy-bz and @Draky50110 \#596 [\#611](https://github.com/wallabag/wallabag/pull/611) ([nicosomb](https://github.com/nicosomb)) +- remove unnecessary variable declaration [\#609](https://github.com/wallabag/wallabag/pull/609) ([nicosomb](https://github.com/nicosomb)) +- in RSS feed, add link to wallabag URL \#556 [\#606](https://github.com/wallabag/wallabag/pull/606) ([nicosomb](https://github.com/nicosomb)) +- \[fix\] Use of undefined constant ATOM \#604 [\#605](https://github.com/wallabag/wallabag/pull/605) ([nicosomb](https://github.com/nicosomb)) +- postgres sequence error fix, issue \#602 [\#603](https://github.com/wallabag/wallabag/pull/603) ([mariroz](https://github.com/mariroz)) +- wallabag 1.6.0 [\#595](https://github.com/wallabag/wallabag/pull/595) ([nicosomb](https://github.com/nicosomb)) +- import into wallabag without cron and file type [\#594](https://github.com/wallabag/wallabag/pull/594) ([mariroz](https://github.com/mariroz)) +- fix of bug with default theme constant [\#588](https://github.com/wallabag/wallabag/pull/588) ([mariroz](https://github.com/mariroz)) +- problem with resources and localhost url fix [\#587](https://github.com/wallabag/wallabag/pull/587) ([mariroz](https://github.com/mariroz)) +- wallabag a link in wallabag now in default theme, issue \#505 [\#586](https://github.com/wallabag/wallabag/pull/586) ([mariroz](https://github.com/mariroz)) +- updated german localisation [\#585](https://github.com/wallabag/wallabag/pull/585) ([kevmeyer](https://github.com/kevmeyer)) +- fix of localhost cookie related to nginx binding to 0.0.0.0 [\#582](https://github.com/wallabag/wallabag/pull/582) ([mariroz](https://github.com/mariroz)) +- fix of issue under nginx and php-fpm [\#581](https://github.com/wallabag/wallabag/pull/581) ([mariroz](https://github.com/mariroz)) +- fix of \_SESSION - indefined variable [\#576](https://github.com/wallabag/wallabag/pull/576) ([mariroz](https://github.com/mariroz)) +- fix of \#115, server relater config value added [\#572](https://github.com/wallabag/wallabag/pull/572) ([mariroz](https://github.com/mariroz)) +- fix of \#115 - stay connected and session livetime [\#567](https://github.com/wallabag/wallabag/pull/567) ([mariroz](https://github.com/mariroz)) +- Update brazilian traslation title to "Português \(Brasil\)" [\#566](https://github.com/wallabag/wallabag/pull/566) ([brunelli](https://github.com/brunelli)) +- Update brazilian translation [\#563](https://github.com/wallabag/wallabag/pull/563) ([brunelli](https://github.com/brunelli)) +- saving link form now sends ajax request, article view is now justified \(issue \#554\) [\#557](https://github.com/wallabag/wallabag/pull/557) ([mariroz](https://github.com/mariroz)) +- feature \#505 - it is now possible to add link from bagged article \(TODO: redev it to ajax action\). Some enhancements to "save a link" popup div [\#551](https://github.com/wallabag/wallabag/pull/551) ([mariroz](https://github.com/mariroz)) +- fix of conflict of s shortcat and entering s in input fields [\#549](https://github.com/wallabag/wallabag/pull/549) ([mariroz](https://github.com/mariroz)) +- a lot of enhancements related to tags: tags list is now sorted, shows number of articles, autocomplete added according to \#477, \#542 [\#544](https://github.com/wallabag/wallabag/pull/544) ([mariroz](https://github.com/mariroz)) +- bagit link + overlay save link + listmode [\#540](https://github.com/wallabag/wallabag/pull/540) ([courgette](https://github.com/courgette)) +- Changed config section to clarify import process. [\#535](https://github.com/wallabag/wallabag/pull/535) ([bobmaerten](https://github.com/bobmaerten)) +- fix of \#530 - import fail from Poche [\#533](https://github.com/wallabag/wallabag/pull/533) ([mariroz](https://github.com/mariroz)) +- New import system [\#532](https://github.com/wallabag/wallabag/pull/532) ([nicosomb](https://github.com/nicosomb)) +- getPageContent moved to Tools, fix of \#426 [\#531](https://github.com/wallabag/wallabag/pull/531) ([mariroz](https://github.com/mariroz)) +- updated german localization [\#524](https://github.com/wallabag/wallabag/pull/524) ([kevmeyer](https://github.com/kevmeyer)) +- effect of block rebuilding on next page removed, issue \#479 [\#517](https://github.com/wallabag/wallabag/pull/517) ([mariroz](https://github.com/mariroz)) +- fix of \#509, related to php 5.3 [\#515](https://github.com/wallabag/wallabag/pull/515) ([mariroz](https://github.com/mariroz)) +- Brazilian portuguese translation [\#513](https://github.com/wallabag/wallabag/pull/513) ([brunelli](https://github.com/brunelli)) +- some baggy theme fixes and enhancement: issue \#479 [\#510](https://github.com/wallabag/wallabag/pull/510) ([mariroz](https://github.com/mariroz)) +- translation related: how-to md file added, script to generate php from all twig templates, polish mo file compiled [\#504](https://github.com/wallabag/wallabag/pull/504) ([mariroz](https://github.com/mariroz)) +- Update pl\_PL.utf8.po [\#502](https://github.com/wallabag/wallabag/pull/502) ([skibbipl](https://github.com/skibbipl)) +- fix of \#498, \#493, \#494 - because disappeared in last commit [\#501](https://github.com/wallabag/wallabag/pull/501) ([mariroz](https://github.com/mariroz)) +- French translation added by @Draky50110, issue \#488 [\#499](https://github.com/wallabag/wallabag/pull/499) ([mariroz](https://github.com/mariroz)) +- fix of global $http visibility, issues \#493, \#494 [\#495](https://github.com/wallabag/wallabag/pull/495) ([mariroz](https://github.com/mariroz)) +- 1.5.2 [\#481](https://github.com/wallabag/wallabag/pull/481) ([nicosomb](https://github.com/nicosomb)) +- a lot of refactoring: tag action is now handled by home view and uses sorting and pagination. Some small view enhacenments. Fix of \#476, \#461 for baggy and other themes [\#478](https://github.com/wallabag/wallabag/pull/478) ([mariroz](https://github.com/mariroz)) +- Baggy Theme - PtSans font is now local [\#473](https://github.com/wallabag/wallabag/pull/473) ([nsteinmetz](https://github.com/nsteinmetz)) +- Full-Text RSS included as a script instead of file\_get\_contents call. Tnx to @Faless. Fix issues \#366 and \#463 [\#472](https://github.com/wallabag/wallabag/pull/472) ([mariroz](https://github.com/mariroz)) +- version number 1.5.1 [\#469](https://github.com/wallabag/wallabag/pull/469) ([nicosomb](https://github.com/nicosomb)) +- 1.5.1 [\#468](https://github.com/wallabag/wallabag/pull/468) ([nicosomb](https://github.com/nicosomb)) +- "save a link" added to top menu \(default theme\) to toggle "save link" form, issue \#461 [\#467](https://github.com/wallabag/wallabag/pull/467) ([mariroz](https://github.com/mariroz)) +- duplicate check added. fix of issue \#400 [\#460](https://github.com/wallabag/wallabag/pull/460) ([mariroz](https://github.com/mariroz)) +- Feature \#457 : import from pocket now import tags too [\#459](https://github.com/wallabag/wallabag/pull/459) ([arnaudmm](https://github.com/arnaudmm)) +- 1.5.0 [\#450](https://github.com/wallabag/wallabag/pull/450) ([nicosomb](https://github.com/nicosomb)) +- Adding .htaccess files to prohibit access to critical directories, e.g., db/ [\#439](https://github.com/wallabag/wallabag/pull/439) ([flolauck](https://github.com/flolauck)) +- Polish and Ukrainian translations added. Russian - updated. Plust 2 smal... [\#435](https://github.com/wallabag/wallabag/pull/435) ([mariroz](https://github.com/mariroz)) +- Theme courgette [\#434](https://github.com/wallabag/wallabag/pull/434) ([courgette](https://github.com/courgette)) +- all locale files re-compiled: fix of \#416 Some language problems in the ... [\#428](https://github.com/wallabag/wallabag/pull/428) ([mariroz](https://github.com/mariroz)) +- changelog 1.4 [\#427](https://github.com/wallabag/wallabag/pull/427) ([nicosomb](https://github.com/nicosomb)) +- Courgette theme revision [\#425](https://github.com/wallabag/wallabag/pull/425) ([Lonnytunes](https://github.com/Lonnytunes)) +- fix of bug \#368 Endless redirects or user doesn't exist with basic authe... [\#419](https://github.com/wallabag/wallabag/pull/419) ([mariroz](https://github.com/mariroz)) +- Fix: stops multiplication, in database, of a same user config item [\#410](https://github.com/wallabag/wallabag/pull/410) ([Lonnytunes](https://github.com/Lonnytunes)) +- Page data extraction with basic auth [\#394](https://github.com/wallabag/wallabag/pull/394) ([aaa2000](https://github.com/aaa2000)) +- Correction de bugs dans le schema mysql [\#391](https://github.com/wallabag/wallabag/pull/391) ([Newinx](https://github.com/Newinx)) +- poche 1.3.1 [\#387](https://github.com/wallabag/wallabag/pull/387) ([nicosomb](https://github.com/nicosomb)) +- Create sqlite table tags\_entries only if not already exists [\#382](https://github.com/wallabag/wallabag/pull/382) ([aaa2000](https://github.com/aaa2000)) +- Flattr Class : Bug 359 [\#381](https://github.com/wallabag/wallabag/pull/381) ([tcitworld](https://github.com/tcitworld)) +- Change Permissions in pochePictures.php [\#380](https://github.com/wallabag/wallabag/pull/380) ([F1reF0x](https://github.com/F1reF0x)) +- Minor changes to tag edit and config pages [\#379](https://github.com/wallabag/wallabag/pull/379) ([williamtheaker](https://github.com/williamtheaker)) +- docs link, typos [\#370](https://github.com/wallabag/wallabag/pull/370) ([DmitrySandalov](https://github.com/DmitrySandalov)) +- poche 1.3.0 [\#363](https://github.com/wallabag/wallabag/pull/363) ([nicosomb](https://github.com/nicosomb)) +- fix for long lasting session [\#362](https://github.com/wallabag/wallabag/pull/362) ([DmitrySandalov](https://github.com/DmitrySandalov)) +- tags: mysql create tables if not exists [\#360](https://github.com/wallabag/wallabag/pull/360) ([DmitrySandalov](https://github.com/DmitrySandalov)) +- Edited English text [\#358](https://github.com/wallabag/wallabag/pull/358) ([williamtheaker](https://github.com/williamtheaker)) +- Tags feature [\#356](https://github.com/wallabag/wallabag/pull/356) ([nicosomb](https://github.com/nicosomb)) +- \[change\] we now use Full-Text RSS 3.1, thank you so much @fivefilters [\#353](https://github.com/wallabag/wallabag/pull/353) ([nicosomb](https://github.com/nicosomb)) +- updating the "es\_ES" locale [\#352](https://github.com/wallabag/wallabag/pull/352) ([versvs](https://github.com/versvs)) +- add atom feeds for unread / favs [\#350](https://github.com/wallabag/wallabag/pull/350) ([nicosomb](https://github.com/nicosomb)) +- Stackoverflow parsing and subdomain failover fix. [\#349](https://github.com/wallabag/wallabag/pull/349) ([JasonGhent](https://github.com/JasonGhent)) +- Fix links [\#347](https://github.com/wallabag/wallabag/pull/347) ([evgeni](https://github.com/evgeni)) +- \[add\] preparing to poche a new link [\#337](https://github.com/wallabag/wallabag/pull/337) ([nicosomb](https://github.com/nicosomb)) +- \[fix\] paths of assets fixed [\#336](https://github.com/wallabag/wallabag/pull/336) ([nicosomb](https://github.com/nicosomb)) +- \[add\] layout [\#331](https://github.com/wallabag/wallabag/pull/331) ([nicosomb](https://github.com/nicosomb)) +- Create fa\_IR.utf8.po [\#329](https://github.com/wallabag/wallabag/pull/329) ([mabkenar](https://github.com/mabkenar)) +- 1.2.0 [\#326](https://github.com/wallabag/wallabag/pull/326) ([nicosomb](https://github.com/nicosomb)) +- \[v2\] ru\_RU locale file [\#315](https://github.com/wallabag/wallabag/pull/315) ([DmitrySandalov](https://github.com/DmitrySandalov)) +- \[v2\] fix \#270 access from remote machine [\#313](https://github.com/wallabag/wallabag/pull/313) ([DmitrySandalov](https://github.com/DmitrySandalov)) +- dark theme images replaced to visible set, no executable bits for pictures [\#310](https://github.com/wallabag/wallabag/pull/310) ([jn0](https://github.com/jn0)) +- Added possibility to mark items as read/unread on the item page itself. [\#307](https://github.com/wallabag/wallabag/pull/307) ([baurmatt](https://github.com/baurmatt)) +- Autoclose [\#291](https://github.com/wallabag/wallabag/pull/291) ([banux](https://github.com/banux)) +- 1.1.0 [\#289](https://github.com/wallabag/wallabag/pull/289) ([nicosomb](https://github.com/nicosomb)) +- Adding support for http\_auth [\#285](https://github.com/wallabag/wallabag/pull/285) ([dsacchet](https://github.com/dsacchet)) +- Updated Screenshots URLs [\#278](https://github.com/wallabag/wallabag/pull/278) ([tcitworld](https://github.com/tcitworld)) +- Update select theme filter and sort names \(dev branch PR\) [\#274](https://github.com/wallabag/wallabag/pull/274) ([NumEricR](https://github.com/NumEricR)) +- Emdeded Roboto webfont [\#273](https://github.com/wallabag/wallabag/pull/273) ([jcsaaddupuy](https://github.com/jcsaaddupuy)) +- Custom ssl port [\#272](https://github.com/wallabag/wallabag/pull/272) ([jcsaaddupuy](https://github.com/jcsaaddupuy)) +- Embeding jquery [\#264](https://github.com/wallabag/wallabag/pull/264) ([jcsaaddupuy](https://github.com/jcsaaddupuy)) +- Fix \#255 : increase article toolbar height if necessary [\#261](https://github.com/wallabag/wallabag/pull/261) ([NumEricR](https://github.com/NumEricR)) +- merge 1.0.0 [\#256](https://github.com/wallabag/wallabag/pull/256) ([nicosomb](https://github.com/nicosomb)) +- Force download on poche export [\#250](https://github.com/wallabag/wallabag/pull/250) ([NumEricR](https://github.com/NumEricR)) +- Avoid ".git" option in themes list of config page [\#245](https://github.com/wallabag/wallabag/pull/245) ([NumEricR](https://github.com/NumEricR)) +- Changed Flattr Caching System [\#244](https://github.com/wallabag/wallabag/pull/244) ([tcitworld](https://github.com/tcitworld)) +- beta5 [\#226](https://github.com/wallabag/wallabag/pull/226) ([nicosomb](https://github.com/nicosomb)) +- Fix \#183: language declaration [\#221](https://github.com/wallabag/wallabag/pull/221) ([NumEricR](https://github.com/NumEricR)) +- Move icon's no-repeat to common code and clean CSS [\#216](https://github.com/wallabag/wallabag/pull/216) ([NumEricR](https://github.com/NumEricR)) +- Flattr [\#214](https://github.com/wallabag/wallabag/pull/214) ([tcitworld](https://github.com/tcitworld)) +- Add url directly [\#212](https://github.com/wallabag/wallabag/pull/212) ([tcitworld](https://github.com/tcitworld)) +- Less is more :-\) [\#205](https://github.com/wallabag/wallabag/pull/205) ([NumEricR](https://github.com/NumEricR)) +- Add the number of results next to pager [\#199](https://github.com/wallabag/wallabag/pull/199) ([NumEricR](https://github.com/NumEricR)) +- Display a message when there is no link [\#191](https://github.com/wallabag/wallabag/pull/191) ([NumEricR](https://github.com/NumEricR)) +- beta4 [\#181](https://github.com/wallabag/wallabag/pull/181) ([nicosomb](https://github.com/nicosomb)) +- Externalize some CSS code [\#180](https://github.com/wallabag/wallabag/pull/180) ([NumEricR](https://github.com/NumEricR)) +- Add support for URLS with digits [\#174](https://github.com/wallabag/wallabag/pull/174) ([nicofrand](https://github.com/nicofrand)) +- Graceful error-handling with imports and defining where import files are stored [\#173](https://github.com/wallabag/wallabag/pull/173) ([EliasZ](https://github.com/EliasZ)) +- Login button [\#170](https://github.com/wallabag/wallabag/pull/170) ([NumEricR](https://github.com/NumEricR)) +- Entries height with short description [\#169](https://github.com/wallabag/wallabag/pull/169) ([NumEricR](https://github.com/NumEricR)) +- Clean up the templates markup [\#168](https://github.com/wallabag/wallabag/pull/168) ([nicofrand](https://github.com/nicofrand)) +- Improve tools list code [\#163](https://github.com/wallabag/wallabag/pull/163) ([NumEricR](https://github.com/NumEricR)) +- Add a print stylesheet [\#160](https://github.com/wallabag/wallabag/pull/160) ([nicofrand](https://github.com/nicofrand)) +- CSS clean up on style.css file [\#159](https://github.com/wallabag/wallabag/pull/159) ([NumEricR](https://github.com/NumEricR)) +- Dev [\#154](https://github.com/wallabag/wallabag/pull/154) ([nicofrand](https://github.com/nicofrand)) +- beta3 [\#141](https://github.com/wallabag/wallabag/pull/141) ([nicosomb](https://github.com/nicosomb)) +- beta3 [\#140](https://github.com/wallabag/wallabag/pull/140) ([nicosomb](https://github.com/nicosomb)) +- merge with beta2 [\#132](https://github.com/wallabag/wallabag/pull/132) ([nicosomb](https://github.com/nicosomb)) +- merge dev into master [\#109](https://github.com/wallabag/wallabag/pull/109) ([nicosomb](https://github.com/nicosomb)) +- Twig [\#104](https://github.com/wallabag/wallabag/pull/104) ([nicosomb](https://github.com/nicosomb)) +- v0.2.1 [\#74](https://github.com/wallabag/wallabag/pull/74) ([nicosomb](https://github.com/nicosomb)) +- Style de l'écran de connexion [\#72](https://github.com/wallabag/wallabag/pull/72) ([Silvus](https://github.com/Silvus)) +- tag 0.2 [\#67](https://github.com/wallabag/wallabag/pull/67) ([nicosomb](https://github.com/nicosomb)) +- Thème dark [\#61](https://github.com/wallabag/wallabag/pull/61) ([Silvus](https://github.com/Silvus)) +- Ajout conf nginx pour la protection de la bdd [\#50](https://github.com/wallabag/wallabag/pull/50) ([Gregoire-M](https://github.com/Gregoire-M)) +- Modifications css [\#19](https://github.com/wallabag/wallabag/pull/19) ([tmos](https://github.com/tmos)) +- Some fixes and improvements [\#5](https://github.com/wallabag/wallabag/pull/5) ([fatihorhan](https://github.com/fatihorhan)) +- filter for entries with previewPicture [\#1418](https://github.com/wallabag/wallabag/pull/1418) ([nicosomb](https://github.com/nicosomb)) +- fix \#972: add preview pictures [\#1417](https://github.com/wallabag/wallabag/pull/1417) ([nicosomb](https://github.com/nicosomb)) +- filters view on side for baggy [\#1404](https://github.com/wallabag/wallabag/pull/1404) ([tcitworld](https://github.com/tcitworld)) +- Cleanup baggy [\#1401](https://github.com/wallabag/wallabag/pull/1401) ([j0k3r](https://github.com/j0k3r)) +- fix \#1357, truncate domain name if it's too loooong [\#1400](https://github.com/wallabag/wallabag/pull/1400) ([nicosomb](https://github.com/nicosomb)) +- Integrate graby [\#1397](https://github.com/wallabag/wallabag/pull/1397) ([j0k3r](https://github.com/j0k3r)) +- Apply margin only on settings page [\#1396](https://github.com/wallabag/wallabag/pull/1396) ([j0k3r](https://github.com/j0k3r)) +- fix \#1378: nice display for tags list [\#1395](https://github.com/wallabag/wallabag/pull/1395) ([nicosomb](https://github.com/nicosomb)) +- Fix date filter on same day [\#1393](https://github.com/wallabag/wallabag/pull/1393) ([j0k3r](https://github.com/j0k3r)) +- Fix redirect after deletion [\#1392](https://github.com/wallabag/wallabag/pull/1392) ([j0k3r](https://github.com/j0k3r)) +- remove dead code [\#1389](https://github.com/wallabag/wallabag/pull/1389) ([nicosomb](https://github.com/nicosomb)) +- WIP – Fixing things around :dash: [\#1386](https://github.com/wallabag/wallabag/pull/1386) ([j0k3r](https://github.com/j0k3r)) +- filters: implement status filter and a new view \(to display all entries\) [\#1385](https://github.com/wallabag/wallabag/pull/1385) ([nicosomb](https://github.com/nicosomb)) +- fix \#1371 config screen: display bug in RSS tab [\#1384](https://github.com/wallabag/wallabag/pull/1384) ([nicosomb](https://github.com/nicosomb)) +- CS [\#1383](https://github.com/wallabag/wallabag/pull/1383) ([j0k3r](https://github.com/j0k3r)) +- fix \#1368 config: redirect on correct tab in material theme [\#1375](https://github.com/wallabag/wallabag/pull/1375) ([nicosomb](https://github.com/nicosomb)) +- filters: enhance view for domain name [\#1374](https://github.com/wallabag/wallabag/pull/1374) ([nicosomb](https://github.com/nicosomb)) +- assign tags to an entry [\#1372](https://github.com/wallabag/wallabag/pull/1372) ([nicosomb](https://github.com/nicosomb)) +- fix 1356: diet for composer [\#1370](https://github.com/wallabag/wallabag/pull/1370) ([nicosomb](https://github.com/nicosomb)) +- fix \#1332: bottom bar to display message [\#1369](https://github.com/wallabag/wallabag/pull/1369) ([nicosomb](https://github.com/nicosomb)) +- action labels depend on status [\#1367](https://github.com/wallabag/wallabag/pull/1367) ([nicosomb](https://github.com/nicosomb)) +- fix tag view in material theme [\#1366](https://github.com/wallabag/wallabag/pull/1366) ([nicosomb](https://github.com/nicosomb)) +- change \ into \ [\#1365](https://github.com/wallabag/wallabag/pull/1365) ([nicosomb](https://github.com/nicosomb)) +- add creation date filter view [\#1363](https://github.com/wallabag/wallabag/pull/1363) ([nicosomb](https://github.com/nicosomb)) +- implement FosUser [\#1362](https://github.com/wallabag/wallabag/pull/1362) ([nicosomb](https://github.com/nicosomb)) +- clean -\> clear [\#1355](https://github.com/wallabag/wallabag/pull/1355) ([nicosomb](https://github.com/nicosomb)) +- some cleanup [\#1354](https://github.com/wallabag/wallabag/pull/1354) ([nicosomb](https://github.com/nicosomb)) +- check if one entry or not [\#1353](https://github.com/wallabag/wallabag/pull/1353) ([nicosomb](https://github.com/nicosomb)) +- fix \#1350: fix pagination with filters [\#1352](https://github.com/wallabag/wallabag/pull/1352) ([nicosomb](https://github.com/nicosomb)) +- add link to original article in material theme [\#1349](https://github.com/wallabag/wallabag/pull/1349) ([nicosomb](https://github.com/nicosomb)) +- \[WIP\] filter by creation date [\#1343](https://github.com/wallabag/wallabag/pull/1343) ([nicosomb](https://github.com/nicosomb)) +- fix \#981: add filter on domain name [\#1342](https://github.com/wallabag/wallabag/pull/1342) ([nicosomb](https://github.com/nicosomb)) +- capistrano config [\#1341](https://github.com/wallabag/wallabag/pull/1341) ([nicosomb](https://github.com/nicosomb)) +- change title for material pages / about page [\#1338](https://github.com/wallabag/wallabag/pull/1338) ([nicosomb](https://github.com/nicosomb)) +- store domainName in database [\#1334](https://github.com/wallabag/wallabag/pull/1334) ([nicosomb](https://github.com/nicosomb)) +- disable download links on entry view [\#1331](https://github.com/wallabag/wallabag/pull/1331) ([nicosomb](https://github.com/nicosomb)) +- fix \#1324: do not display pagination when only 1 page [\#1328](https://github.com/wallabag/wallabag/pull/1328) ([nicosomb](https://github.com/nicosomb)) +- fix \#1299: change send buttons on config page [\#1326](https://github.com/wallabag/wallabag/pull/1326) ([nicosomb](https://github.com/nicosomb)) +- fix \#1314: change max-width in material theme [\#1325](https://github.com/wallabag/wallabag/pull/1325) ([nicosomb](https://github.com/nicosomb)) +- fix \#1302: add 'stay connected' checkbox [\#1323](https://github.com/wallabag/wallabag/pull/1323) ([nicosomb](https://github.com/nicosomb)) +- fix \#1303, add link to homepage on forgot password page [\#1322](https://github.com/wallabag/wallabag/pull/1322) ([nicosomb](https://github.com/nicosomb)) +- add new post form in material header [\#1319](https://github.com/wallabag/wallabag/pull/1319) ([nicosomb](https://github.com/nicosomb)) +- fix link in howto page [\#1317](https://github.com/wallabag/wallabag/pull/1317) ([nicosomb](https://github.com/nicosomb)) +- fix \#1310 add focus to search form [\#1316](https://github.com/wallabag/wallabag/pull/1316) ([nicosomb](https://github.com/nicosomb)) +- fix \#1311, redirect after login [\#1312](https://github.com/wallabag/wallabag/pull/1312) ([nicosomb](https://github.com/nicosomb)) +- rename favorite in starred [\#1304](https://github.com/wallabag/wallabag/pull/1304) ([nicosomb](https://github.com/nicosomb)) +- store estimated reading time in database \(\#393\) [\#1297](https://github.com/wallabag/wallabag/pull/1297) ([nicosomb](https://github.com/nicosomb)) +- fix \#564: replace Untitled by the domain name [\#1296](https://github.com/wallabag/wallabag/pull/1296) ([nicosomb](https://github.com/nicosomb)) +- add howto page [\#1295](https://github.com/wallabag/wallabag/pull/1295) ([nicosomb](https://github.com/nicosomb)) +- fix \#1282: add title on wallabag logo [\#1294](https://github.com/wallabag/wallabag/pull/1294) ([nicosomb](https://github.com/nicosomb)) +- get itemsPerPage for unread/fav/archive views [\#1293](https://github.com/wallabag/wallabag/pull/1293) ([nicosomb](https://github.com/nicosomb)) +- Add tags list display [\#1292](https://github.com/wallabag/wallabag/pull/1292) ([nicosomb](https://github.com/nicosomb)) +- fix \#1283: display the good title for each category [\#1286](https://github.com/wallabag/wallabag/pull/1286) ([nicosomb](https://github.com/nicosomb)) +- set material to default theme \o/ [\#1279](https://github.com/wallabag/wallabag/pull/1279) ([nicosomb](https://github.com/nicosomb)) +- Added Material theme [\#1276](https://github.com/wallabag/wallabag/pull/1276) ([modos189](https://github.com/modos189)) +- Adds pagerfanta paginator everywhere, modifies article routing. [\#1250](https://github.com/wallabag/wallabag/pull/1250) ([frankoa](https://github.com/frankoa)) +- V2 Edit title [\#1208](https://github.com/wallabag/wallabag/pull/1208) ([j0k3r](https://github.com/j0k3r)) +- Upgrade to Symfony 2.7 [\#1206](https://github.com/wallabag/wallabag/pull/1206) ([j0k3r](https://github.com/j0k3r)) +- changed email fields with the email type and added required parameter on... [\#1180](https://github.com/wallabag/wallabag/pull/1180) ([tcitworld](https://github.com/tcitworld)) +- Move API stuff in ApiBundle [\#1167](https://github.com/wallabag/wallabag/pull/1167) ([j0k3r](https://github.com/j0k3r)) +- Add RSS feeds [\#1166](https://github.com/wallabag/wallabag/pull/1166) ([j0k3r](https://github.com/j0k3r)) +- Prefix table [\#1165](https://github.com/wallabag/wallabag/pull/1165) ([j0k3r](https://github.com/j0k3r)) +- Remove ability to change username [\#1164](https://github.com/wallabag/wallabag/pull/1164) ([j0k3r](https://github.com/j0k3r)) +- Speed up tests [\#1163](https://github.com/wallabag/wallabag/pull/1163) ([j0k3r](https://github.com/j0k3r)) +- Handle forgot password [\#1152](https://github.com/wallabag/wallabag/pull/1152) ([j0k3r](https://github.com/j0k3r)) +- Simplify build system [\#1144](https://github.com/wallabag/wallabag/pull/1144) ([j0k3r](https://github.com/j0k3r)) +- V2 theme [\#1132](https://github.com/wallabag/wallabag/pull/1132) ([j0k3r](https://github.com/j0k3r)) +- \[WIP\] API : hypermedia & tags [\#1110](https://github.com/wallabag/wallabag/pull/1110) ([nicosomb](https://github.com/nicosomb)) +- V2 config [\#1095](https://github.com/wallabag/wallabag/pull/1095) ([j0k3r](https://github.com/j0k3r)) +- Add more tests to Entry controller + security [\#1072](https://github.com/wallabag/wallabag/pull/1072) ([j0k3r](https://github.com/j0k3r)) +- 1st draft for testing API [\#1070](https://github.com/wallabag/wallabag/pull/1070) ([nicosomb](https://github.com/nicosomb)) +- GET /api/entries returns object, no more array [\#1069](https://github.com/wallabag/wallabag/pull/1069) ([nicosomb](https://github.com/nicosomb)) +- V2 api authentication [\#1068](https://github.com/wallabag/wallabag/pull/1068) ([nicosomb](https://github.com/nicosomb)) +- rename Tags entity [\#1063](https://github.com/wallabag/wallabag/pull/1063) ([nicosomb](https://github.com/nicosomb)) +- sort entries in repository [\#1048](https://github.com/wallabag/wallabag/pull/1048) ([nicosomb](https://github.com/nicosomb)) -### Added -- [#1962](https://github.com/wallabag/wallabag/pull/1962) cURL examples in documentation about API (Dirk Deimeke) -### Fixed - -- Forgot `composer.lock` file in 2.0.2 release so some users may encounter `Fatal error: Out of memory` error during installation - -## [2.0.2] - 2016-04-21 - -### Added - -- [#1945](https://github.com/wallabag/wallabag/pull/1945) cURL examples in documentation about API (Dirk Deimeke) -- [#1911](https://github.com/wallabag/wallabag/pull/1911) Add title hover on entry titles (Thomas Citharel) - -### Changed - -- [#1944](https://github.com/wallabag/wallabag/pull/1944), [#1953](https://github.com/wallabag/wallabag/pull/1953), [#1954](https://github.com/wallabag/wallabag/pull/1954) Due to branches renaming, update documentation and configuration (maxi62330, Nicolas Lœuillet) -- [#1942](https://github.com/wallabag/wallabag/pull/1942) Optimize import (Nicolas Lœuillet) -- [#1935](https://github.com/wallabag/wallabag/pull/1935) Update spanish translation (jami7) -- [#1929](https://github.com/wallabag/wallabag/pull/1929) Change guid and link in RSS feeds to give original entry URL (Nicolas Lœuillet) -- [#1918](https://github.com/wallabag/wallabag/pull/1918) Improve pagination when user has lot of entries (Nicolas Lœuillet) -- [#1916](https://github.com/wallabag/wallabag/pull/1916) Change PHP arrays and move test parameters in a separated file (Jeremy Benoist) - -### Fixed - -- [#1957](https://github.com/wallabag/wallabag/pull/1957) Fix translation for `Go to your account` button (Nicolas Lœuillet) -- [#1925](https://github.com/wallabag/wallabag/pull/1925) Redirect to homepage if refered is null (Nicolas Lœuillet) -- [#1912](https://github.com/wallabag/wallabag/pull/1912) Fix 500 Internal Server Error -> "Setting piwik_enabled couldn't be found" (Jeremy Benoist) - -## [2.0.1] - 2016-04-11 -### Added - -- [Documentation about importing large file](http://doc.wallabag.org/en/v2/user/installation.html#installing-on-nginx) into nginx. (Nicolas Lœuillet) -- [Documentation about wallabag API](http://doc.wallabag.org/en/v2/developer/api.html) (Nicolas Lœuillet) -- [#1861](https://github.com/wallabag/wallabag/pull/1861) Round estimated time and add reading speed for Baggy (Nicolas Lœuillet) -- [Documentation about wallabag v1 CLI import](http://doc.wallabag.org/en/v2/user/migration.html#import-via-command-line-interface-cli) (Nicolas Lœuillet) -- [Add migrate link](http://doc.wallabag.org/en/v2/user/migration.html) in documentation (Nicolas Lœuillet) - -### Changed - -- [#1823](https://github.com/wallabag/wallabag/pull/1823) Persian translation (Masoud Abkenar) -- [#1901](https://github.com/wallabag/wallabag/pull/1901) Spanish translation (Jeremy Benoist) -- [#1879](https://github.com/wallabag/wallabag/pull/1879) Move tag form in Material theme (Nicolas Lœuillet) - -### Fixed - -- [#1903](https://github.com/wallabag/wallabag/pull/1903) Force server version to avoid connection error (Jeremy Benoist) -- [#1887](https://github.com/wallabag/wallabag/pull/1887) Fix duplicate article when added via the bookmarklet (Nicolas Lœuillet) -- [#1895](https://github.com/wallabag/wallabag/pull/1895) API: All the entries are fetched via GET /api/entries (Nicolas Lœuillet) -- [#1898](https://github.com/wallabag/wallabag/pull/1898) Fix estimated reading time in material view #1893 (Nicolas Lœuillet) -- [#1896](https://github.com/wallabag/wallabag/pull/1896) remove download_picture setting and manage show_printlink in material theme #1867 (Nicolas Lœuillet) -- [#1894](https://github.com/wallabag/wallabag/pull/1894) Fix documentation link in developer page (Nicolas Lœuillet) -- [#1891](https://github.com/wallabag/wallabag/pull/1891) Fix typo on configuration page (Nicolas Lœuillet) -- [#1884](https://github.com/wallabag/wallabag/pull/1884) Change the installation method in issue template (Nicolas Lœuillet) -- [#1844](https://github.com/wallabag/wallabag/pull/1844) Lock ocramius/proxy-manager (Jeremy Benoist) -- [#1840](https://github.com/wallabag/wallabag/pull/1840) Fix Archive page title translation (Nicolas Lœuillet) -- [#1801](https://github.com/wallabag/wallabag/pull/1804) Force user-agent for iansommerville.com (Jeremy Benoist) - -### Removed - -- [#1900](https://github.com/wallabag/wallabag/pull/1900) Remove empty portugese documentation (Nicolas Lœuillet) -- [#1868](https://github.com/wallabag/wallabag/pull/1868) Remove keyboard shortcut and add tagging rule panel in baggy (Nicolas Lœuillet) -- [#1824](https://github.com/wallabag/wallabag/pull/1824) Remove SMTP configuration environment overrides (Mathieu Bruyen) - -## [2.0.0] - 2016-04-03 -### Added - -* save an article, read it, favorite it, archive it. (Hopefully) -* annotations: In each article you read, you can write annotations. ([read the doc](http://doc.wallabag.org/en/v2/user/annotations.html)) -* filter entries by reading time, domain name, creation date, status, etc. -* assign tags to entries -* edit article titles -* a REST API ([you can have a look to the documentation](http://v2.wallabag.org/api/doc)) -* authorization via oAuth2 -* a new default theme, called `material` -* RSS feeds (with ability to limit number of articles) -* create a new account from the config page (for super admin only) -* recover passwords from login page (you have to fill your email on config page) -* picture preview, if available, is displayed for each entry -* Public registration -* migration from wallabag v1/v2 (based on JSON export) ([read the doc](http://doc.wallabag.org/en/v2/user/import.html)) -* migration from Pocket (it works, but we need to implement asynchronous import: if you have too many articles, it can fail) ([read the doc](http://doc.wallabag.org/en/v2/user/import.html)) -* exports in many formats (PDF, JSON, EPUB, MOBI, XML, CSV and TXT). -* 2-Factor authentication via email ([read the doc](http://doc.wallabag.org/en/v2/user/configuration.html#two-factor-authentication)) -* Tagging rule: create a rule to automatically assign tags to entries! ([read the doc](http://doc.wallabag.org/en/v2/user/configuration.html#tagging-rules)) -* Occitan, German, French, Turkish, Persian, Romanian, Polish, Danish, Spanish and English translations -* Quickstart for beginners (when you don't have any entries) -* Internal settings for administrator (the account created during installation) -* For 3rd apps developers, a developer page is available to create API token +\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 000000000..72a407f92 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,225 @@ +module.exports = function (grunt) { + require('load-grunt-tasks')(grunt); + + grunt.initConfig({ + appDir: 'app/Resources/static', + buildDir: 'app/Resources/build', + modulesDir: 'node_modules', + releaseDir: 'web/bundles/wallabagcore', + + postcss: { + material: { + options: { + processors: [ + require('pixrem')(), + require('autoprefixer')({ browsers: 'last 2 versions' }), + require('cssnano')(), + ], + }, + src: '<%= buildDir %>/material.css', + dest: '<%= releaseDir %>/themes/material/css/style.min.css', + }, + baggy: { + options: { + processors: [ + require('pixrem')(), + require('autoprefixer')({ browsers: 'last 2 versions' }), + require('cssnano')(), + ], + }, + src: '<%= buildDir %>/baggy.css', + dest: '<%= releaseDir %>/themes/baggy/css/style.min.css', + }, + }, + concat: { + options: { + separator: ';', + }, + cssMaterial: { + src: [ + 'node_modules/materialize-css/bin/materialize.css', + '<%= appDir %>/themes/material/css/*.css', + ], + dest: '<%= buildDir %>/material.css', + }, + cssBaggy: { + src: [ + '<%= appDir %>/themes/baggy/css/*.css', + ], + dest: '<%= buildDir %>/baggy.css', + }, + }, + browserify: { + dist: { + files: { + '<%= buildDir %>/material.browser.js': ['<%= appDir %>/themes/material/js/init.js'], + '<%= buildDir %>/baggy.browser.js': ['<%= appDir %>/themes/baggy/js/init.js'] + } + }, + options: { + sourceType: "module", + transform: [ + ["babelify", { + presets: ["es2015"] + }], ["browserify-shim", { + "jquery": { + "exports": "$" + }, + "materialize": "materialize", + "jquery-ui": { + "depends": "jquery", + "exports": null + } + }] + ], + browserifyOptions: { + browser: { + "jQuery": "./node_modules/jquery/dist/jquery.js", + "jquery.tinydot": "./node_modules/jquery.tinydot/src/jquery.tinydot.js", + "jquery.ui": "./node_modules/jquery-ui-browserify/dist/jquery-ui.js" + } + } + } + + }, + uglify: { + material: { + files: { + '<%= releaseDir %>/themes/material/js/material.min.js': + ['<%= buildDir %>/material.browser.js'], + } + }, + baggy: { + files: { + '<%= releaseDir %>/themes/baggy/js/baggy.min.js': + ['<%= buildDir %>/baggy.browser.js'], + } + }, + }, + copy: { + pickerjs: { + expand: true, + cwd: '<%= modulesDir %>/pickadate/lib', + src: 'picker.js', + dest: '<%= buildDir %>', + }, + annotator: { + expand: true, + cwd: '<%= modulesDir %>/annotator/pkg', + src: 'annotator.min.js', + dest: '<%= buildDir %>/themes/_global/js/', + }, + baggyfonts: { + files: [ + { + expand: true, + cwd: '<%= modulesDir %>/icomoon-free-npm/Font', + src: 'IcoMoon-Free.ttf', + dest: '<%= releaseDir %>/themes/baggy/fonts/', + }, + { + expand: true, + cwd: '<%= modulesDir %>/ptsans-npm-webfont/fonts', + src: 'ptsansbold.woff', + dest: '<%= releaseDir %>/themes/baggy/fonts/', + }, + { + expand: true, + cwd: '<%= modulesDir %>/material-design-icons-iconfont/dist/fonts/', + src: ['MaterialIcons-Regular.eot', 'MaterialIcons-Regular.woff2', 'MaterialIcons-Regular.woff', 'MaterialIcons-Regular.ttf'], + dest: '<%= releaseDir %>/themes/baggy/fonts/', + }, + ], + }, + materialfonts: { + files: [ + { + expand: true, + overwrite: true, + cwd: '<%= modulesDir %>/icomoon-free-npm/Font', + src: 'IcoMoon-Free.ttf', + dest: '<%= releaseDir %>/themes/material/fonts', + }, + { + expand: true, + overwrite: true, + cwd: '<%= modulesDir %>/roboto-fontface/fonts/Roboto', + src: '*', + dest: '<%= releaseDir %>/themes/material/font/roboto', + }, + { + expand: true, + overwrite: true, + cwd: '<%= modulesDir %>/material-design-icons-iconfont/dist/fonts/', + src: ['MaterialIcons-Regular.eot', 'MaterialIcons-Regular.woff2', 'MaterialIcons-Regular.woff', 'MaterialIcons-Regular.ttf'], + dest: '<%= releaseDir %>/themes/material/fonts/', + }, + ], + }, + }, + symlink: { + pics: { + files: [ + { + expand: true, + overwrite: true, + cwd: '<%= appDir %>/themes/_global/', + src: 'img', + dest: '<%= releaseDir %>/themes/_global/', + }, + ], + }, + }, + clean: { + css: { + src: ['<%= buildDir %>/**/*.css'], + }, + js: { + src: ['<%= buildDir %>/**/*.js', '<%= buildDir %>/**/*.map'], + }, + all: { + src: ['./<%= buildDir %>'], + }, + release: { + src: ['./<%= releaseDir %>/*'], + } + }, + eslint: { + target: ['<%= appDir %>/themes/material/js/init.js', '<%= appDir %>/themes/baggy/js/init.js'] + }, + stylelint: { + target: ['<%= appDir %>/themes/material/css/*.css', '<%= appDir %>/themes/baggy/css/*.css'] + } + + }); + + grunt.registerTask( + 'fonts', + 'Install fonts', + ['copy:baggyfonts', 'copy:materialfonts'] + ); + + grunt.registerTask( + 'js', + 'Build and install js files', + ['clean:js', 'copy:pickerjs', 'browserify', 'uglify'] + ); + + grunt.registerTask( + 'default', + 'Build and install everything', + ['clean', 'copy:pickerjs', 'concat', 'browserify', 'uglify', 'postcss', 'copy', 'symlink'] + ); + + grunt.registerTask( + 'css', + 'Compiles the stylesheets.', + ['clean:css', 'concat:cssMaterial', 'concat:cssBaggy', 'postcss'] + ); + + grunt.registerTask( + 'tests', + 'Test css and js style conformity', + ['eslint', 'stylelint', 'default'] + ) +}; diff --git a/Makefile b/Makefile new file mode 100755 index 000000000..1df4d6327 --- /dev/null +++ b/Makefile @@ -0,0 +1,46 @@ +TMP_FOLDER=/tmp +RELEASE_FOLDER=wllbg-release + +ifndef ENV + ENV=prod +endif + +help: ## Display this help menu + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +clean: ## Clear the application cache + @rm -rf var/cache/* + +install: ## Install wallabag with the latest version + @sh scripts/install.sh $(ENV) + +update: ## Update the wallabag installation to the latest version + @sh scripts/update.sh $(ENV) + +dev: ## Install the latest dev version + @sh scripts/dev.sh + +run: ## Run the wallabag built-in server + @php bin/console server:run --env=$(ENV) + +build: ## Run grunt + @grunt + +test: ## Launch wallabag testsuite + @if [ ! -d "vendor/phpunit" ]; then composer install; fi + @ant prepare && vendor/phpunit/phpunit/phpunit -v + +release: ## Create a package. Need a VERSION parameter (eg: `make release VERSION=master`). +ifndef VERSION + $(error VERSION is not set) +endif + @sh scripts/release.sh $(VERSION) $(TMP_FOLDER) $(RELEASE_FOLDER) $(ENV) + +travis: ## Make some stuff for Travis-CI + +deploy: ## Deploy wallabag + @bundle exec cap staging deploy + +.PHONY: help clean install update build test release travis deploy + +.DEFAULT_GOAL := install diff --git a/README.md b/README.md index 2ce6479ef..c94882884 100644 --- a/README.md +++ b/README.md @@ -6,23 +6,22 @@ wallabag is a self hostable application allowing you to not miss any content anymore. Click, save and read it when you can. It extracts content so that you can read it when you have time. -More informations on our website: [wallabag.org](https://wallabag.org) +More information on our website: [wallabag.org](https://wallabag.org). + +![wallabag logo](https://raw.githubusercontent.com/wallabag/logo/master/_default/typo-horizontal/png/sm/logo-typo-horizontal-black-no-bg-no-border-sm.png) # Install wallabag +Please read [the documentation to see the wallabag requirements](http://doc.wallabag.org/en/master/user/installation.html#requirements). -If you don't have it yet, please [install composer](https://getcomposer.org/download/) or be sure to use Composer 1.2 (`composer selfupdate` can help you about that). Then you can install wallabag by executing the following commands: ``` - git clone https://github.com/wallabag/wallabag.git - cd wallabag - git checkout 2.0.8 - SYMFONY_ENV=prod composer install --no-dev -o --prefer-dist - php bin/console wallabag:install --env=prod - php bin/console server:run --env=prod +git clone https://github.com/wallabag/wallabag.git +cd wallabag && make install +make run ``` -## License +# License Copyright © 2013-2016 Nicolas Lœuillet This work is free. You can redistribute it and/or modify it under the terms of the MIT License. See the COPYING file for more details. diff --git a/RELEASE_PROCESS.md b/RELEASE_PROCESS.md index 45e605462..34e9a36f2 100644 --- a/RELEASE_PROCESS.md +++ b/RELEASE_PROCESS.md @@ -1,47 +1,45 @@ ## Definition A release is mostly a git tag of http://github.com/wallabag/wallabag, following [semantic versioning](http://semver.org). -The last release at the time of writing is 2.0.0-alpha.2, from the v2 branch. ### Steps to release -During this documentation, we assume the release is `release-2.0.0-beta.1`. +During this documentation, we assume the release is `$LAST_WALLABAG_RELEASE`. #### Files to edit - `app/config/config.yml` (`wallabag_core.version`) -- `README.md` (`composer create-project` command) -- `docs/en/user/installation.rst` and its translations (`composer create-project` command) +- `CHANGELOG.md` (by using this command `github_changelog_generator --no-compare-link --header-label="# Changelog" --no-issues --no-pr-wo-labels --since-tag="1.9.2"`. [github-changelog-generator is available here](https://github.com/skywinder/github-changelog-generator)) #### Create release on GitHub - Run these commands to create the tag: ``` - git checkout v2 - git pull origin v2 - git checkout -b release-2.0.0-beta.1 - SYMFONY_ENV=prod composer up --no-dev - git add --force composer.lock - git add README.md - git commit -m "Release wallabag 2.0.0-beta.1" - git push origin release-2.0.0-beta.1 +git checkout master +git pull origin master +git checkout -b release-$LAST_WALLABAG_RELEASE +SYMFONY_ENV=prod composer up --no-dev +git add --force composer.lock +git commit -m "Release wallabag $LAST_WALLABAG_RELEASE" +git push origin release-$LAST_WALLABAG_RELEASE ``` -- Create a new pull request with this title `DON'T MERGE Release wallabag 2.0.0-beta.1`. This pull request is used to launch builds on Travis-CI. -- Run these commands to create the package: +- Create a new pull request with this title `DON'T MERGE Release wallabag $LAST_WALLABAG_RELEASE`. This pull request is used to launch builds on Travis-CI. +- Run these command to create the package: ``` - git clone git@github.com:wallabag/wallabag.git -b release-2.0.0-beta.1 release-2.0.0-beta.1 - SYMFONY_ENV=prod composer up -d=release-2.0.0-beta.1 --no-dev - tar czf wallabag-release-2.0.0-beta.1.tar.gz --exclude="var/*" --exclude=".git" release-2.0.0-beta.1 +make release master /tmp wllbg-release prod ``` - [Create the new release on GitHub](https://github.com/wallabag/wallabag/releases/new). You have to upload on this page the package. -- Delete the `release-2.0.0-beta.1` branch and close the pull request (**DO NOT MERGE IT**). +- Delete the `release-$LAST_WALLABAG_RELEASE` branch and close the pull request (**DO NOT MERGE IT**). - Update the URL shortener (used on `wllbg.org` to generate links like `http://wllbg.org/latest-v2-package` or `http://wllbg.org/latest-v2`) - Update [the downloads page](https://github.com/wallabag/wallabag.org/blob/master/content/pages/download.md) on the website (MD5 sum, release date) -- Drink a beer! +- Update Dockerfile https://github.com/wallabag/docker (and create a new tag) +- Update wallabag.org website (downloads, releases and new blog post) +- Put the next patch version suffixed with `-dev` in `app/config/config.yml` (`wallabag_core.version`) +- Drink a :beer:! ### `composer.lock` A release tag must contain a `composer.lock` file. It sets which dependencies were available at the time a release was done, diff --git a/app/AppKernel.php b/app/AppKernel.php index 04f86eb76..52f855585 100644 --- a/app/AppKernel.php +++ b/app/AppKernel.php @@ -13,7 +13,6 @@ class AppKernel extends Kernel new Symfony\Bundle\TwigBundle\TwigBundle(), new Symfony\Bundle\MonologBundle\MonologBundle(), new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(), - new Symfony\Bundle\AsseticBundle\AsseticBundle(), new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(), new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(), new FOS\RestBundle\FOSRestBundle(), @@ -39,6 +38,7 @@ class AppKernel extends Kernel new Wallabag\UserBundle\WallabagUserBundle(), new Wallabag\ImportBundle\WallabagImportBundle(), new Wallabag\AnnotationBundle\WallabagAnnotationBundle(), + new OldSound\RabbitMqBundle\OldSoundRabbitMqBundle(), ]; if (in_array($this->getEnvironment(), ['dev', 'test'], true)) { diff --git a/app/DoctrineMigrations/Version20160410190541.php b/app/DoctrineMigrations/Version20160410190541.php new file mode 100644 index 000000000..f034b0e46 --- /dev/null +++ b/app/DoctrineMigrations/Version20160410190541.php @@ -0,0 +1,51 @@ +container = $container; + } + + private function getTable($tableName) + { + return $this->container->getParameter('database_table_prefix') . $tableName; + } + + /** + * @param Schema $schema + */ + public function up(Schema $schema) + { + if ($this->connection->getDatabasePlatform()->getName() == 'postgresql') { + $this->addSql('ALTER TABLE "'.$this->getTable('entry').'" ADD uuid UUID DEFAULT NULL'); + } else { + $this->addSql('ALTER TABLE "'.$this->getTable('entry').'" ADD uuid LONGTEXT DEFAULT NULL'); + } + + $this->addSql("INSERT INTO \"".$this->getTable('craue_config_setting')."\" (name, value, section) VALUES ('share_public', '1', 'entry')"); + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + $this->abortIf($this->connection->getDatabasePlatform()->getName() != 'sqlite', 'This down migration can\'t be executed on SQLite databases, because SQLite don\'t support DROP COLUMN.'); + + $this->addSql('ALTER TABLE "'.$this->getTable('entry').'" DROP uuid'); + $this->addSql("DELETE FROM \"".$this->getTable('craue_config_setting')."\" WHERE name = 'share_public'"); + } +} diff --git a/app/DoctrineMigrations/Version20160812120952.php b/app/DoctrineMigrations/Version20160812120952.php new file mode 100644 index 000000000..3aafea644 --- /dev/null +++ b/app/DoctrineMigrations/Version20160812120952.php @@ -0,0 +1,53 @@ +container = $container; + } + + private function getTable($tableName) + { + return $this->container->getParameter('database_table_prefix') . $tableName; + } + + /** + * @param Schema $schema + */ + public function up(Schema $schema) + { + switch ($this->connection->getDatabasePlatform()->getName()) { + case 'sqlite': + $this->addSql('ALTER TABLE '.$this->getTable('oauth2_clients').' ADD name longtext DEFAULT NULL'); + break; + case 'mysql': + $this->addSql('ALTER TABLE '.$this->getTable('oauth2_clients').' ADD name longtext COLLATE \'utf8_unicode_ci\' DEFAULT NULL'); + break; + case 'postgresql': + $this->addSql('ALTER TABLE '.$this->getTable('oauth2_clients').' ADD name text DEFAULT NULL'); + } + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + $this->abortIf($this->connection->getDatabasePlatform()->getName() == 'sqlite', 'Migration can only be executed safely on \'mysql\' or \'postgresql\'.'); + + $this->addSql('ALTER TABLE '.$this->getTable('oauth2_clients').' DROP COLUMN name'); + } +} diff --git a/app/DoctrineMigrations/Version20160911214952.php b/app/DoctrineMigrations/Version20160911214952.php new file mode 100644 index 000000000..f14f7bc6f --- /dev/null +++ b/app/DoctrineMigrations/Version20160911214952.php @@ -0,0 +1,42 @@ +container = $container; + } + + private function getTable($tableName) + { + return $this->container->getParameter('database_table_prefix') . $tableName; + } + + /** + * @param Schema $schema + */ + public function up(Schema $schema) + { + $this->addSql('INSERT INTO "'.$this->getTable('craue_config_setting').'" (name, value, section) VALUES (\'import_with_redis\', \'0\', \'import\')'); + $this->addSql('INSERT INTO "'.$this->getTable('craue_config_setting').'" (name, value, section) VALUES (\'import_with_rabbitmq\', \'0\', \'import\')'); + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + } +} diff --git a/app/DoctrineMigrations/Version20160916201049.php b/app/DoctrineMigrations/Version20160916201049.php new file mode 100644 index 000000000..0d2edf9ef --- /dev/null +++ b/app/DoctrineMigrations/Version20160916201049.php @@ -0,0 +1,46 @@ +container = $container; + } + + private function getTable($tableName) + { + return $this->container->getParameter('database_table_prefix') . $tableName; + } + + /** + * @param Schema $schema + */ + public function up(Schema $schema) + { + $this->addSql('ALTER TABLE "'.$this->getTable('config').'" ADD pocket_consumer_key VARCHAR(255) DEFAULT NULL'); + $this->addSql("DELETE FROM \"".$this->getTable('craue_config_setting')."\" WHERE name = 'pocket_consumer_key';"); + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + $this->abortIf($this->connection->getDatabasePlatform()->getName() == 'sqlite', 'Migration can only be executed safely on \'mysql\' or \'postgresql\'.'); + + $this->addSql('ALTER TABLE "'.$this->getTable('config').'" DROP pocket_consumer_key'); + $this->addSql("INSERT INTO \"".$this->getTable('craue_config_setting')."\" (name, value, section) VALUES ('pocket_consumer_key', NULL, 'import')"); + } +} diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.da.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.da.yml index c46fdb142..85079330f 100644 --- a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.da.yml +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.da.yml @@ -8,7 +8,8 @@ export_csv: Aktiver eksport til CSV export_json: Aktiver eksport til JSON export_txt: Aktiver eksport til TXT export_xml: Aktiver eksport til XML -pocket_consumer_key: Brugers nøgle til Pocket for at importere materialer (https://getpocket.com/developer/docs/authentication) +# import_with_rabbitmq: Enable RabbitMQ to import data asynchronously +# import_with_redis: Enable Redis to import data asynchronously shaarli_url: Shaarli-URL, hvis tjenesten er aktiv share_diaspora: Aktiver deling til Diaspora share_mail: Aktiver deling med email @@ -27,3 +28,4 @@ piwik_site_id: ID for din side hos Piwik piwik_enabled: Aktiver Piwik demo_mode_enabled: "Aktiver demo-indstilling? (anvendes kun til wallabags offentlige demo)" demo_mode_username: "Demobruger" +# share_public: Allow public url for entries diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.de.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.de.yml index fa2093b52..b3412af7f 100644 --- a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.de.yml +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.de.yml @@ -2,13 +2,14 @@ download_pictures: Bilder auf den Server herunterladen carrot: Teilen zu Carrot aktivieren diaspora_url: Diaspora-URL, sofern der Service aktiviert ist export_epub: ePUB-Export aktivieren -export_mobi: .mobi-Export aktivieren +export_mobi: mobi-Export aktivieren export_pdf: PDF-Export aktivieren export_csv: CSV-Export aktivieren export_json: JSON-Export aktivieren export_txt: TXT-Export aktivieren export_xml: XML-Export aktivieren -pocket_consumer_key: Consumer-Key für Pocket, um Inhalte zu importieren (https://getpocket.com/developer/docs/authentication) +import_with_rabbitmq: Aktiviere RabbitMQ, um Artikel asynchron zu importieren +import_with_redis: Aktiviere Redis, um Artikel asynchron zu importieren shaarli_url: Shaarli-URL, sofern der Service aktiviert ist share_diaspora: Teilen zu Diaspora aktiveren share_mail: Teilen via E-Mail aktiveren @@ -27,3 +28,4 @@ piwik_site_id: ID deiner Webseite in Piwik piwik_enabled: Piwik aktivieren demo_mode_enabled: "Test-Modus aktivieren? (nur für die öffentliche wallabag-Demo genutzt)" demo_mode_username: "Test-Benutzer" +share_public: Erlaube eine öffentliche URL für Einträge diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.en.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.en.yml index b627376ec..8aaa27e76 100644 --- a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.en.yml +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.en.yml @@ -8,7 +8,8 @@ export_csv: Enable CSV export export_json: Enable JSON export export_txt: Enable TXT export export_xml: Enable XML export -pocket_consumer_key: Consumer key for Pocket to import contents (https://getpocket.com/developer/docs/authentication) +import_with_rabbitmq: Enable RabbitMQ to import data asynchronously +import_with_redis: Enable Redis to import data asynchronously shaarli_url: Shaarli URL, if the service is enabled share_diaspora: Enable share to Diaspora share_mail: Enable share by email @@ -27,3 +28,4 @@ piwik_site_id: ID of your website in Piwik piwik_enabled: Enable Piwik demo_mode_enabled: "Enable demo mode ? (only used for the wallabag public demo)" demo_mode_username: "Demo user" +share_public: Allow public url for entries diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.es.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.es.yml index af1657e4a..bf3a79af8 100644 --- a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.es.yml +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.es.yml @@ -8,7 +8,8 @@ export_csv: Activar exportación a CSV export_json: Activar exportación a JSON export_txt: Activar exportación a TXT export_xml: Activar exportación a XML -pocket_consumer_key: Consumer key for Pocket to import contents (https://getpocket.com/developer/docs/authentication) +# import_with_rabbitmq: Enable RabbitMQ to import data asynchronously +# import_with_redis: Enable Redis to import data asynchronously shaarli_url: Shaarli URL, si el servicio está activado share_diaspora: Activar compartir con Diaspora share_mail: Activar compartir con email @@ -27,3 +28,4 @@ piwik_site_id: ID de tu website de Piwik piwik_enabled: Activar Piwik demo_mode_enabled: "Activar modo demo (sólo usado para la demo de wallabag)" demo_mode_username: "Nombre de usuario demo" +# share_public: Allow public url for entries diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.fa.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.fa.yml index 7cb4e833a..d86c43433 100644 --- a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.fa.yml +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.fa.yml @@ -8,7 +8,8 @@ export_csv: فعال‌سازی برون‌سپاری به CSV export_json: فعال‌سازی برون‌سپاری به JSON export_txt: فعال‌سازی برون‌سپاری به TXT export_xml: فعال‌سازی برون‌سپاری به XML -pocket_consumer_key: کلید کاربری Pocket برای درون‌ریزی مطالب (https://getpocket.com/developer/docs/authentication) +# import_with_rabbitmq: Enable RabbitMQ to import data asynchronously +# import_with_redis: Enable Redis to import data asynchronously shaarli_url: نشانی Shaarli، اگر فعال بود share_diaspora: فعال‌سازی هم‌رسانی به Diaspora share_mail: فعال‌سازی هم‌رسانی با ایمیل @@ -22,3 +23,9 @@ export: "برون‌سپاری" import: "درون‌ریزی" misc: "غیره" modify_settings: "اعمال" +# piwik_host: Host of your website in Piwik +# piwik_site_id: ID of your website in Piwik +# piwik_enabled: Enable Piwik +# demo_mode_enabled: "Enable demo mode ? (only used for the wallabag public demo)" +# demo_mode_username: "Demo user" +# share_public: Allow public url for entries diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.fr.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.fr.yml index 084eb6df1..5e1ecf26c 100644 --- a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.fr.yml +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.fr.yml @@ -8,7 +8,8 @@ export_csv: Activer l'export CSV export_json: Activer l'export JSON export_txt: Activer l'export TXT export_xml: Activer l'export XML -pocket_consumer_key: Clé d'authentification Pocket pour importer les données (https://getpocket.com/developer/docs/authentication) +import_with_rabbitmq: Activer RabbitMQ pour gérer les imports de façon asynchrone +import_with_redis: Activer Redis pour gérer les imports de façon asynchrone shaarli_url: URL de Shaarli, si le service Shaarli est activé share_diaspora: Activer le partage vers Diaspora share_mail: Activer le partage par email @@ -27,3 +28,4 @@ piwik_site_id: ID de votre site dans Piwik piwik_enabled: Activer Piwik demo_mode_enabled: "Activer le mode démo ? (utiliser uniquement pour la démo publique de wallabag)" demo_mode_username: "Utilisateur de la démo" +share_public: Autoriser une URL publique pour les articles diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.it.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.it.yml index e44171393..9d820e4bc 100644 --- a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.it.yml +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.it.yml @@ -8,7 +8,8 @@ export_csv: Abilita esportazione CSV export_json: Abilita esportazione JSON export_txt: Abilita esportazione TXT export_xml: Abilita esportazione XML -pocket_consumer_key: Consumer key per Pocket per importare i contenuti (https://getpocket.com/developer/docs/authentication) +# import_with_rabbitmq: Enable RabbitMQ to import data asynchronously +# import_with_redis: Enable Redis to import data asynchronously shaarli_url: Shaarli URL, se il servizio è abilitato share_diaspora: Abilita la condivisione con Diaspora share_mail: Abilita la condivisione per email @@ -27,3 +28,4 @@ piwik_site_id: ID del tuo sito in Piwik piwik_enabled: Abilita Piwik demo_mode_enabled: "Abilita modalità demo ? (usato solo per la demo pubblica di wallabag)" demo_mode_username: "Utente Demo" +# share_public: Allow public url for entries diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.oc.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.oc.yml index 67880b8bb..49dc77326 100644 --- a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.oc.yml +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.oc.yml @@ -8,7 +8,8 @@ export_csv: Activar l'expòrt CSV export_json: Activar l'expòrt JSON export_txt: Activar l'expòrt TXT export_xml: Activar l'expòrt XML -pocket_consumer_key: Clau d'autentificacion Pocket per importar las donadas (https://getpocket.com/developer/docs/authentication) +# import_with_rabbitmq: Enable RabbitMQ to import data asynchronously +# import_with_redis: Enable Redis to import data asynchronously shaarli_url: URL de Shaarli, se lo servici Shaarli es activat share_diaspora: Activar lo partatge cap a Diaspora share_mail: Activar lo partatge per corrièl @@ -27,3 +28,4 @@ piwik_site_id: ID de vòstre site dins Piwik piwik_enabled: Activar Piwik demo_mode_enabled: "Activar lo mode demostracion ? (utilizar solament per la demostracion publica de wallabag)" demo_mode_username: "Utilizaire de la demostracion" +# share_public: Allow public url for entries diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.pl.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.pl.yml index 87ca5060e..7432b0a79 100644 --- a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.pl.yml +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.pl.yml @@ -8,7 +8,8 @@ export_csv: Włącz eksport do CSV export_json: Włącz eksport do JSON export_txt: Włącz eksport do TXT export_xml: Włącz eksport do XML -pocket_consumer_key: Klucz klienta Pocket do importu zawartości (https://getpocket.com/developer/docs/authentication) +import_with_rabbitmq: Włącz RabbitMQ dla asynchronicznego importu danych +import_with_redis: Włącz Redis dla asynchronicznego importu danych shaarli_url: Adress URL Shaarli, jeżeli usługa jest włączona share_diaspora: Włącz udostępnianie dla Diaspora share_mail: Włącz udostępnianie przez email @@ -22,3 +23,9 @@ export: "eksport" import: "import" misc: "różne" modify_settings: "zatwierdz" +piwik_host: Host twojej strony Piwik +piwik_site_id: ID twojej strony Piwik +piwik_enabled: Włacz Piwik +demo_mode_enabled: "Włacz tryb demo? (używany wyłącznie dla publicznej demonstracji Wallabag)" +demo_mode_username: "Użytkownik Demonstracyjny" +share_public: Zezwalaj na publiczny adres url dla wpisow diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.ro.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.ro.yml index f5ff8c6a4..20d664f7e 100644 --- a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.ro.yml +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.ro.yml @@ -8,7 +8,8 @@ export_csv: Permite exportare CSV export_json: Permite exportare JSON export_txt: Permite exportare TXT export_xml: Permite exportare XML -pocket_consumer_key: Cheie consumator pentru importarea contentului din Pocket (https://getpocket.com/developer/docs/authentication) +# import_with_rabbitmq: Enable RabbitMQ to import data asynchronously +# import_with_redis: Enable Redis to import data asynchronously shaarli_url: Shaarli URL, dacă serviciul este permis share_diaspora: Permite share către Diaspora share_mail: Permite share prin email @@ -22,3 +23,9 @@ export: "exportă" import: "importă" misc: "diverse" modify_settings: "aplică" +# piwik_host: Host of your website in Piwik +# piwik_site_id: ID of your website in Piwik +# piwik_enabled: Enable Piwik +# demo_mode_enabled: "Enable demo mode ? (only used for the wallabag public demo)" +# demo_mode_username: "Demo user" +# share_public: Allow public url for entries diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.tr.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.tr.yml new file mode 100644 index 000000000..4b4d3edb9 --- /dev/null +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.tr.yml @@ -0,0 +1,31 @@ +# download_pictures: Download pictures on your server +# carrot: Enable share to Carrot +# diaspora_url: Diaspora URL, if the service is enabled +# export_epub: Enable ePub export +# export_mobi: Enable .mobi export +# export_pdf: Enable PDF export +# export_csv: Enable CSV export +# export_json: Enable JSON export +# export_txt: Enable TXT export +# export_xml: Enable XML export +# import_with_rabbitmq: Enable RabbitMQ to import data asynchronously +# import_with_redis: Enable Redis to import data asynchronously +# shaarli_url: Shaarli URL, if the service is enabled +# share_diaspora: Enable share to Diaspora +# share_mail: Enable share by email +# share_shaarli: Enable share to Shaarli +# share_twitter: Enable share to Twitter +# show_printlink: Display a link to print content +# wallabag_support_url: Support URL for wallabag +# wallabag_url: URL of *your* wallabag instance +# entry: "article" +# export: "export" +# import: "import" +# misc: "misc" +# modify_settings: "apply" +# piwik_host: Host of your website in Piwik +# piwik_site_id: ID of your website in Piwik +# piwik_enabled: Enable Piwik +# demo_mode_enabled: "Enable demo mode ? (only used for the wallabag public demo)" +# demo_mode_username: "Demo user" +# share_public: Allow public url for entries diff --git a/app/Resources/CraueConfigBundle/views/Settings/modify.html.twig b/app/Resources/CraueConfigBundle/views/Settings/modify.html.twig index f44db420f..c4e735ee0 100644 --- a/app/Resources/CraueConfigBundle/views/Settings/modify.html.twig +++ b/app/Resources/CraueConfigBundle/views/Settings/modify.html.twig @@ -1,6 +1,6 @@ {% extends "WallabagCoreBundle::layout.html.twig" %} -{% block title %}{% trans %}internal settings{% endtrans %}{% endblock %} +{% block title %}{{ 'menu.left.internal_settings'|trans }}{% endblock %} {% block content %}
@@ -13,7 +13,7 @@
diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon-114.png b/app/Resources/static/themes/_global/img/appicon/apple-touch-icon-114.png similarity index 100% rename from src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon-114.png rename to app/Resources/static/themes/_global/img/appicon/apple-touch-icon-114.png diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon-120.png b/app/Resources/static/themes/_global/img/appicon/apple-touch-icon-120.png similarity index 100% rename from src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon-120.png rename to app/Resources/static/themes/_global/img/appicon/apple-touch-icon-120.png diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon-144.png b/app/Resources/static/themes/_global/img/appicon/apple-touch-icon-144.png similarity index 100% rename from src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon-144.png rename to app/Resources/static/themes/_global/img/appicon/apple-touch-icon-144.png diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon-152.png b/app/Resources/static/themes/_global/img/appicon/apple-touch-icon-152.png similarity index 100% rename from src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon-152.png rename to app/Resources/static/themes/_global/img/appicon/apple-touch-icon-152.png diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon-57.png b/app/Resources/static/themes/_global/img/appicon/apple-touch-icon-57.png similarity index 100% rename from src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon-57.png rename to app/Resources/static/themes/_global/img/appicon/apple-touch-icon-57.png diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon-72.png b/app/Resources/static/themes/_global/img/appicon/apple-touch-icon-72.png similarity index 100% rename from src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon-72.png rename to app/Resources/static/themes/_global/img/appicon/apple-touch-icon-72.png diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon-76.png b/app/Resources/static/themes/_global/img/appicon/apple-touch-icon-76.png similarity index 100% rename from src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon-76.png rename to app/Resources/static/themes/_global/img/appicon/apple-touch-icon-76.png diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon.png b/app/Resources/static/themes/_global/img/appicon/apple-touch-icon.png similarity index 100% rename from src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon.png rename to app/Resources/static/themes/_global/img/appicon/apple-touch-icon.png diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/favicon.ico b/app/Resources/static/themes/_global/img/appicon/favicon.ico similarity index 100% rename from src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/favicon.ico rename to app/Resources/static/themes/_global/img/appicon/favicon.ico diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/baggy/img/bg-select.png b/app/Resources/static/themes/_global/img/bg-select.png similarity index 100% rename from src/Wallabag/CoreBundle/Resources/public/themes/baggy/img/bg-select.png rename to app/Resources/static/themes/_global/img/bg-select.png diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/_global/img/icons/carrot-icon--black.png b/app/Resources/static/themes/_global/img/icons/carrot-icon--black.png similarity index 100% rename from src/Wallabag/CoreBundle/Resources/public/themes/_global/img/icons/carrot-icon--black.png rename to app/Resources/static/themes/_global/img/icons/carrot-icon--black.png diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/_global/img/icons/carrot-icon--white.png b/app/Resources/static/themes/_global/img/icons/carrot-icon--white.png similarity index 100% rename from src/Wallabag/CoreBundle/Resources/public/themes/_global/img/icons/carrot-icon--white.png rename to app/Resources/static/themes/_global/img/icons/carrot-icon--white.png diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/_global/img/icons/diaspora-icon--black.png b/app/Resources/static/themes/_global/img/icons/diaspora-icon--black.png similarity index 100% rename from src/Wallabag/CoreBundle/Resources/public/themes/_global/img/icons/diaspora-icon--black.png rename to app/Resources/static/themes/_global/img/icons/diaspora-icon--black.png diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/_global/img/icons/diaspora-icon--white.png b/app/Resources/static/themes/_global/img/icons/diaspora-icon--white.png similarity index 100% rename from src/Wallabag/CoreBundle/Resources/public/themes/_global/img/icons/diaspora-icon--white.png rename to app/Resources/static/themes/_global/img/icons/diaspora-icon--white.png diff --git a/app/Resources/static/themes/_global/img/icons/shaarli.png b/app/Resources/static/themes/_global/img/icons/shaarli.png new file mode 100644 index 000000000..0e83c46ee Binary files /dev/null and b/app/Resources/static/themes/_global/img/icons/shaarli.png differ diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/material/img/logo-other_themes.png b/app/Resources/static/themes/_global/img/logo-other_themes.png similarity index 100% rename from src/Wallabag/CoreBundle/Resources/public/themes/material/img/logo-other_themes.png rename to app/Resources/static/themes/_global/img/logo-other_themes.png diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/material/img/logo-square.png b/app/Resources/static/themes/_global/img/logo-square.png similarity index 100% rename from src/Wallabag/CoreBundle/Resources/public/themes/material/img/logo-square.png rename to app/Resources/static/themes/_global/img/logo-square.png diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/baggy/img/logo-w.png b/app/Resources/static/themes/_global/img/logo-w.png similarity index 100% rename from src/Wallabag/CoreBundle/Resources/public/themes/baggy/img/logo-w.png rename to app/Resources/static/themes/_global/img/logo-w.png diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/baggy/img/logo-wallabag.svg b/app/Resources/static/themes/_global/img/logo-wallabag.svg similarity index 100% rename from src/Wallabag/CoreBundle/Resources/public/themes/baggy/img/logo-wallabag.svg rename to app/Resources/static/themes/_global/img/logo-wallabag.svg diff --git a/app/Resources/static/themes/_global/js/bookmarklet.js b/app/Resources/static/themes/_global/js/bookmarklet.js new file mode 100644 index 000000000..5174ff472 --- /dev/null +++ b/app/Resources/static/themes/_global/js/bookmarklet.js @@ -0,0 +1,6 @@ + +top['bookmarklet-url@wallabag.org'] = + 'bag it!' + + '' + + ''; diff --git a/app/Resources/static/themes/_global/js/tools.js b/app/Resources/static/themes/_global/js/tools.js new file mode 100644 index 000000000..ab30deb11 --- /dev/null +++ b/app/Resources/static/themes/_global/js/tools.js @@ -0,0 +1,50 @@ +const $ = require('jquery'); + +function supportsLocalStorage() { + try { + return 'localStorage' in window && window.localStorage !== null; + } catch (e) { + return false; + } +} + +function savePercent(id, percent) { + if (!supportsLocalStorage()) { return false; } + localStorage[`wallabag.article.${id}.percent`] = percent; + return true; +} + +function retrievePercent(id) { + if (!supportsLocalStorage()) { return false; } + + const bheight = $(document).height(); + const percent = localStorage[`wallabag.article.${id}.percent`]; + const scroll = bheight * percent; + + $('html,body').animate({ scrollTop: scroll }, 'fast'); + + return true; +} + +function initFilters() { + // no display if filters not available + if ($('div').is('#filters')) { + $('#button_filters').show(); + $('.button-collapse-right').sideNav({ edge: 'right' }); + $('#clear_form_filters').on('click', () => { + $('#filters input').val(''); + $('#filters :checked').removeAttr('checked'); + return false; + }); + } +} + +function initExport() { + // no display if export not available + if ($('div').is('#export')) { + $('#button_export').show(); + $('.button-collapse-right').sideNav({ edge: 'right' }); + } +} + +export { savePercent, retrievePercent, initFilters, initExport }; diff --git a/app/Resources/static/themes/baggy/css/font.css b/app/Resources/static/themes/baggy/css/font.css new file mode 100755 index 000000000..47edcb838 --- /dev/null +++ b/app/Resources/static/themes/baggy/css/font.css @@ -0,0 +1,6 @@ +@font-face { + font-family: "PT Sans"; + font-style: normal; + font-weight: 700; + src: local("PT Sans Bold"), local("PTSans-Bold"), url("../fonts/ptsansbold.woff") format("woff"); +} diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/baggy/css/main.css b/app/Resources/static/themes/baggy/css/main.css similarity index 53% rename from src/Wallabag/CoreBundle/Resources/public/themes/baggy/css/main.css rename to app/Resources/static/themes/baggy/css/main.css index ba430cc4a..44db2070b 100755 --- a/src/Wallabag/CoreBundle/Resources/public/themes/baggy/css/main.css +++ b/app/Resources/static/themes/baggy/css/main.css @@ -15,7 +15,7 @@ html { } body { - background-color: #EEE; + background-color: #eee; } .login { @@ -28,9 +28,9 @@ body { } .login form { - background-color: #FFF; + background-color: #fff; padding: 1.5em; - box-shadow: 0 1px 8px rgba(0,0,0,0.9); + box-shadow: 0 1px 8px rgba(0, 0, 0, 0.9); width: 20em; position: absolute; top: 8em; @@ -50,7 +50,7 @@ body { ========================================================================== */ ::selection { - color: #FFF; + color: #fff; background-color: #000; } @@ -65,12 +65,16 @@ body { left: 0.6em; } -h2, h3, h4 { - font-family: 'PT Sans', sans-serif; +h2, +h3, +h4 { + font-family: "PT Sans", sans-serif; text-transform: uppercase; } -p, li, label { +p, +li, +label { color: #666; } @@ -79,28 +83,38 @@ a { font-weight: bold; } -a:hover, a:focus { +a.nostyle { + text-decoration: none; +} + +a:hover, +a:focus { text-decoration: none; } form fieldset { - border:0; + border: 0; padding: 0; margin: 0; } -form input[type="text"], form input[type="number"], select, form input[type="password"], form input[type="url"], form input[type="email"] { +form input[type="text"], +form input[type="number"], +select, +form input[type="password"], +form input[type="url"], +form input[type="email"] { border: 1px solid #999; padding: 0.5em 1em; min-width: 12em; color: #666; } -@media screen and (-webkit-min-device-pixel-ratio:0){ - select{ +@media screen and (-webkit-min-device-pixel-ratio: 0) { + select { -webkit-appearance: none; border-radius: 0; - background: #FFF url(../img/bg-select.png) no-repeat right center; + background: #fff url("../../_global/img/bg-select.png") no-repeat right center; } } @@ -127,31 +141,34 @@ form .row { margin-bottom: 0.5em; } -form button, input[type="submit"] { - cursor:pointer; +form button, +input[type="submit"] { + cursor: pointer; background-color: #000; - color: #FFF; - border:0; + color: #fff; padding: 0.5em 1em; display: inline-block; - border:1px solid #000; + border: 1px solid #000; } - form button:hover, form button:focus, input[type="submit"]:hover, input[type="submit"]:focus { - background-color: #FFF; - color: #000; - -webkit-transition: all 0.5s ease; - -moz-transition: all 0.5s ease; - -ms-transition: all 0.5s ease; - -o-transition: all 0.5s ease; - transition: all 0.5s ease; - } +form button:hover, +form button:focus, +input[type="submit"]:hover, +input[type="submit"]:focus { + background-color: #fff; + color: #000; + -webkit-transition: all 0.5s ease; + -moz-transition: all 0.5s ease; + -ms-transition: all 0.5s ease; + -o-transition: all 0.5s ease; + transition: all 0.5s ease; +} #bookmarklet { cursor: move; } -h2:after { +h2::after { content: ""; height: 4px; width: 70px; @@ -163,12 +180,12 @@ h2:after { padding: 0; margin: 0; } - .links li { - list-style: none; - margin: 0; - padding: 0; - } +.links li { + list-style: none; + margin: 0; + padding: 0; +} #links { position: fixed; @@ -179,119 +196,132 @@ h2:after { background-color: #333; padding-top: 9.5em; height: 100%; - box-shadow:inset -4px 0 20px rgba(0,0,0,0.6); + box-shadow: inset -4px 0 20px rgba(0, 0, 0, 0.6); z-index: 15; } #main { - margin-left: 13em; + margin-left: 12em; position: relative; z-index: 10; padding-right: 5%; padding-bottom: 1em; } - #links > li > a { - display: block; - padding: 0.5em 2em 0.5em 1em; - color: #FFF; - position: relative; - text-transform: uppercase; - text-decoration: none; - font-weight: normal; - font-family: 'PT Sans', sans-serif; - -webkit-transition: all 0.5s ease; - -moz-transition: all 0.5s ease; - -ms-transition: all 0.5s ease; - -o-transition: all 0.5s ease; - transition: all 0.5s ease; - } +#links > li > a { + display: block; + padding: 0.5em 2em 0.5em 1em; + color: #fff; + position: relative; + text-transform: uppercase; + text-decoration: none; + font-weight: normal; + font-family: "PT Sans", sans-serif; + -webkit-transition: all 0.5s ease; + -moz-transition: all 0.5s ease; + -ms-transition: all 0.5s ease; + -o-transition: all 0.5s ease; + transition: all 0.5s ease; +} - #links > li > a:hover, #links > li > a:focus { - background-color: #999; - color: #000; - } +#links > li > a:hover, +#links > li > a:focus { + background-color: #999; + color: #000; +} - #links .current:after { - content: ""; - width: 0; - height: 0; - position: absolute; - border-style: solid; - border-width: 10px; - border-color: transparent #EEE transparent transparent; - right: 0; - top: 50%; - margin-top: -10px; - } +#links .current::after { + content: ""; + width: 0; + height: 0; + position: absolute; + border-style: solid; + border-width: 10px; + border-color: transparent #eee transparent transparent; + right: 0; + top: 50%; + margin-top: -10px; +} - #links li:last-child { - position: fixed; - bottom: 1em; - width: 10em; - } - - #links li:last-child a:before { - font-size: 1.2em; - position: relative; - top: 2px; - } +#links li:last-child { + position: fixed; + bottom: 1em; + width: 10em; +} +#links li:last-child a::before { + font-size: 1.2em; + position: relative; + top: 2px; +} #sort { - padding: 0; - list-style-type: none; - opacity: 0.5; - display: inline-block; + padding: 0; + list-style-type: none; + opacity: 0.5; + display: inline-block; } #sort li { - display: inline; - font-size: 0.9em; + display: inline; + font-size: 0.9em; } #sort li + li { - margin-left: 10px; + margin-left: 10px; } #sort a { - padding: 2px 2px 0; - vertical-align: middle; + padding: 2px 2px 0; + vertical-align: middle; } #sort img { - vertical-align: baseline; + vertical-align: baseline; } + #sort img:hover { - cursor: pointer; + cursor: pointer; } #display-mode { float: right; - vertical-align: middle; margin-top: 10px; margin-bottom: 10px; opacity: 0.5; } + #listmode { width: 16px; display: inline-block; text-decoration: none; } + #listmode a:hover { opacity: 1; } + #listmode.tablemode { background-image: url("../img/baggy/table.png"); background-repeat: no-repeat; background-position: bottom; } + #listmode.listmode { background-image: url("../img/baggy/list.png"); background-repeat: no-repeat; background-position: bottom; } +#warning_message { + position: fixed; + background-color: #ff6347; + z-index: 1000; + bottom: 0; + left: 0; + width: 100%; + color: #000; +} /* ========================================================================== 2 = Layout @@ -319,12 +349,64 @@ footer a { } .list-entries { - letter-spacing:-5px; + letter-spacing: -5px; } .listmode .entry { - width: 100%!important; - margin-left: 0!important; + width: 100% !important; + margin-left: 0 !important; +} + +.card-entry-labels { + position: absolute; + top: 100px; + left: -1em; + z-index: 90; + max-width: 50%; + padding-left: 0; +} + +.card-entry-labels li { + margin: 10px 10px 10px auto; + padding: 5px 12px 5px 25px; + background-color: rgba(0, 0, 0, 0.6); + border-radius: 0 3px 3px 0; + color: #fff; + cursor: default; + max-height: 2em; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.card-entry-tags { + max-height: 2em; + overflow-y: hidden; + padding: 0; + margin: 0; +} + +.card-entry-tags li, +.card-entry-tags span { + display: inline-block; + margin: 0 5px; + padding: 5px 12px; + background-color: rgba(0, 0, 0, 0.6); + border-radius: 3px; + max-height: 2em; + overflow: hidden; + text-overflow: ellipsis; +} + +.card-entry-tags a, +.card-entry-labels a { + text-decoration: none; + font-weight: normal; + color: #fff; +} + +.nav-panel-add-tag { + margin-top: 10px; } .list-entries + .results { @@ -344,47 +426,39 @@ footer a { } .entry { - background-color: #FFF; - letter-spacing:normal; - box-shadow: 0 3px 7px rgba(0,0,0,0.3); + background-color: #fff; + letter-spacing: normal; + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); display: inline-block; width: 32%; margin-bottom: 1.5em; vertical-align: top; - margin-left: 1.5%; + margin-right: 1%; position: relative; overflow: hidden; - padding: 1.5em 1.5em 3em 1.5em; - - /* Removing CSS transitions because they make the switch from list view to - * table view jerky - */ - /* -webkit-transition: all 0.5s ease; */ - /* -moz-transition: all 0.5s ease; */ - /* -ms-transition: all 0.5s ease; */ - /* -o-transition: all 0.5s ease; */ - /* transition: all 0.5s ease; */ + padding: 1.5em 1.5em 3em; + height: 440px; } -.entry:before { +.entry::before { content: ""; width: 0; height: 0; - border-style:solid; - border-color: transparent transparent #000 transparent; + border-style: solid; + border-color: transparent transparent #000; border-width: 10px; position: absolute; bottom: 0.3em; z-index: 10; right: 1.5em; -webkit-transition: all 0.5s ease; - -moz-transition: all 0.5s ease; - -ms-transition: all 0.5s ease; - -o-transition: all 0.5s ease; - transition: all 0.5s ease; + -moz-transition: all 0.5s ease; + -ms-transition: all 0.5s ease; + -o-transition: all 0.5s ease; + transition: all 0.5s ease; } -.entry:after { +.entry::after { content: ""; position: absolute; height: 7px; @@ -393,21 +467,21 @@ footer a { left: 0; background-color: #000; -webkit-transition: all 0.5s ease; - -moz-transition: all 0.5s ease; - -ms-transition: all 0.5s ease; - -o-transition: all 0.5s ease; - transition: all 0.5s ease; + -moz-transition: all 0.5s ease; + -ms-transition: all 0.5s ease; + -o-transition: all 0.5s ease; + transition: all 0.5s ease; } .entry:hover { - box-shadow: 0 3px 10px rgba(0,0,0,1); + box-shadow: 0 3px 10px rgba(0, 0, 0, 1); } -.entry:hover:after { +.entry:hover::after { height: 40px; } -.entry:hover:before { +.entry:hover::before { bottom: 2.4em; } @@ -421,10 +495,9 @@ footer a { line-height: 1.2; } - .entry h2:after { - content: none; - } - +.entry h2::after { + content: none; +} .entry h2 a { display: block; @@ -432,35 +505,28 @@ footer a { color: #000; word-wrap: break-word; -webkit-transition: all 0.5s ease; - -moz-transition: all 0.5s ease; - -ms-transition: all 0.5s ease; - -o-transition: all 0.5s ease; - transition: all 0.5s ease; + -moz-transition: all 0.5s ease; + -ms-transition: all 0.5s ease; + -o-transition: all 0.5s ease; + transition: all 0.5s ease; } -/* -.entry h2 a:after { - content: ""; - position: absolute; - top: 0; - width: 100%; - height: 100%; - left: 0; -} -*/ img.preview { - max-width: 100%; + max-width: calc(100% + 3em); + left: -1.5em; + position: relative; } .entry p { color: #666; font-size: 0.9em; line-height: 1.7; + margin-top: 5px; } - .entry h2 a:first-letter { - text-transform: uppercase; - } +.entry h2 a::first-letter { + text-transform: uppercase; +} .entry:hover .tools { bottom: 0; @@ -475,26 +541,26 @@ img.preview { padding-right: 0.5em; text-align: right; -webkit-transition: all 0.5s ease; - -moz-transition: all 0.5s ease; - -ms-transition: all 0.5s ease; - -o-transition: all 0.5s ease; - transition: all 0.5s ease; + -moz-transition: all 0.5s ease; + -ms-transition: all 0.5s ease; + -o-transition: all 0.5s ease; + transition: all 0.5s ease; } - .entry .tools a { - color: #666; - text-decoration: none; - display: block; - padding: 0.4em; - } +.entry .tools a { + color: #666; + text-decoration: none; + display: block; + padding: 0.4em; +} - .entry .tools a:hover { - color: #FFF; - } +.entry .tools a:hover { + color: #fff; +} - .entry .tools li { - display: inline-block; - } +.entry .tools li { + display: inline-block; +} .entry:nth-child(3n+1) { margin-left: 0; @@ -510,11 +576,12 @@ img.preview { vertical-align: top; letter-spacing: normal; width: 50%; + text-align: right; } div.pagination ul { text-align: right; - margin-bottom:50px; + margin-bottom: 50px; } .nb-results { @@ -533,11 +600,13 @@ div.pagination ul a { text-decoration: none; } -div.pagination ul a:hover, div.pagination ul a:focus { - text-decoration: underline; +div.pagination ul a:hover, +div.pagination ul a:focus { + text-decoration: underline; } -div.pagination ul .disabled { +div.pagination ul .prev.disabled, +div.pagination ul .next.disabled { display: none; } @@ -556,7 +625,7 @@ div.pagination ul .current { ========================================================================== */ .popup-form { - background: rgba(0,0,0,0.5); + background: rgba(0, 0, 0, 0.5); position: absolute; top: 0; left: 10em; @@ -567,11 +636,11 @@ div.pagination ul .current { margin-top: -30% !important; padding: 2em; display: none; - border-left: 1px #EEE solid; + border-left: 1px #eee solid; } - .popup-form form { - background-color: #FFF; +.popup-form form { + background-color: #fff; position: absolute; top: 0; left: 0; @@ -580,51 +649,52 @@ div.pagination ul .current { width: 400px; height: 200px; padding: 2em; - } +} #bagit-form-form .addurl { - margin-left: 0; + margin-left: 0; } .closeMessage, .close-button { background-color: #000; - color: #FFF; - font-size: 1.2em; - line-height: 1.6; - width: 1.6em; - height: 1.6em; - text-align: center; + color: #fff; + font-size: 1.2em; + line-height: 1.6; + width: 1.6em; + height: 1.6em; + text-align: center; text-decoration: none; } - .closeMessage:hover, - .closeMessage:focus, - .close-button:hover, - .close-button:focus { + +.closeMessage:hover, +.closeMessage:focus, +.close-button:hover, +.close-button:focus { background-color: #999; color: #000; - } +} .close-button--popup { - display: inline-block; - position: absolute; - top: 0; - right: 0; - font-size: 1.4em; + display: inline-block; + position: absolute; + top: 0; + right: 0; + font-size: 1.4em; } .active-current { background-color: #999; } -.active-current:after { +.active-current::after { content: ""; width: 0; height: 0; position: absolute; border-style: solid; border-width: 10px; - border-color: transparent #EEE transparent transparent; + border-color: transparent #eee transparent transparent; right: 0; top: 50%; margin-top: -10px; @@ -637,26 +707,27 @@ div.pagination ul .current { .add-to-wallabag-link-after { background-color: #000; color: #fff; - padding: 0 3px 2px 3px; + padding: 0 3px 2px; } a.add-to-wallabag-link-after { - visibility: hidden; - position: absolute; - opacity: 0; - transition-duration: 2s; - transition-timing-function: ease-out; + visibility: hidden; + position: absolute; + opacity: 0; + transition-duration: 2s; + transition-timing-function: ease-out; } -#article article a:hover + a.add-to-wallabag-link-after, a.add-to-wallabag-link-after:hover { - opacity: 1; - visibility: visible; - transition-duration: .3s; - transition-timing-function: ease-in; +#article article a:hover + a.add-to-wallabag-link-after, +a.add-to-wallabag-link-after:hover { + opacity: 1; + visibility: visible; + transition-duration: 0.3s; + transition-timing-function: ease-in; } -a.add-to-wallabag-link-after:after { - content: "w"; +a.add-to-wallabag-link-after::after { + content: "w"; } #add-link-result { @@ -664,29 +735,73 @@ a.add-to-wallabag-link-after:after { font-size: 0.9em; } +.btn-clickable { + cursor: pointer; +} + /* ========================================================================== 3 = Pictos ========================================================================== */ @font-face { - font-family: 'icomoon'; - src:url('../fonts/icomoon.eot?-s0mcsx'); - src:url('../fonts/icomoon.eot?#iefix-s0mcsx') format('embedded-opentype'), - url('../fonts/icomoon.woff?-s0mcsx') format('woff'), - url('../fonts/icomoon.ttf?-s0mcsx') format('truetype'), - url('../fonts/icomoon.svg?-s0mcsx#icomoon') format('svg'); + font-family: icomoon; + src: url("../fonts/IcoMoon-Free.ttf"); font-weight: normal; font-style: normal; } +@font-face { + font-family: 'Material Icons'; + font-style: normal; + font-weight: 400; + src: url(../fonts/MaterialIcons-Regular.eot); + + /* For IE6-8 */ + src: local("Material Icons"), local("MaterialIcons-Regular"), url(../fonts/MaterialIcons-Regular.woff2) format("woff2"), url(../fonts/MaterialIcons-Regular.woff) format("woff"), url(../fonts/MaterialIcons-Regular.ttf) format("truetype"); +} + +.material-icons { + font-family: 'Material Icons'; + font-weight: normal; + font-style: normal; + font-size: 1em; /* Preferred icon size */ + width: 1em; + height: 1em; + display: inline-block; + line-height: 1; + text-transform: none; + letter-spacing: normal; + word-wrap: normal; + white-space: nowrap; + direction: ltr; + + /* Support for all WebKit browsers. */ + -webkit-font-smoothing: antialiased; + + /* Support for Safari and Chrome. */ + text-rendering: optimizeLegibility; + + /* Support for Firefox. */ + -moz-osx-font-smoothing: grayscale; + + /* Support for IE. */ + font-feature-settings: 'liga'; +} + +.material-icons.md-18 { font-size: 18px; } +.material-icons.md-24 { font-size: 24px; } +.material-icons.md-36 { font-size: 36px; } +.material-icons.md-48 { font-size: 48px; } + .icon span, .icon-image span { position: absolute; top: -9999px; } -[class^="icon-"]:before, [class*=" icon-"]:before { - font-family: 'icomoon'; +[class^="icon-"]::before, +[class*=" icon-"]::before { + font-family: icomoon; speak: none; font-style: normal; font-weight: normal; @@ -694,98 +809,148 @@ a.add-to-wallabag-link-after:after { text-transform: none; line-height: 1; + /* Enable Ligatures ================ */ + letter-spacing: 0; + -webkit-font-feature-settings: "liga"; + -moz-font-feature-settings: "liga=1"; + -moz-font-feature-settings: "liga"; + -ms-font-feature-settings: "liga" 1; + -o-font-feature-settings: "liga"; + font-feature-settings: "liga"; + /* Better Font Rendering =========== */ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } -.icon-flattr:before { - content: "\e800"; +.icon-flattr::before { + content: "\ead4"; } -.icon-mail:before { - content: "\e80a"; + +.icon-mail::before { + content: "\ea86"; } -.icon-up-open:before { + +.icon-up-open::before { content: "\e80b"; } -.icon-star:before { - content: "\e805"; + +.icon-star::before { + content: "\e9d9"; } -.icon-check:before { - content: "\e804"; + +.icon-check::before { + content: "\ea10"; } -.icon-link:before { - content: "\e801"; + +.icon-link::before { + content: "\e9cb"; } -.icon-reply:before { + +.icon-reply::before { content: "\e806"; } -.icon-menu:before { - content: "\e802"; + +.icon-menu::before { + content: "\e9bd"; } -.icon-clock:before { + +.icon-clock::before { content: "\e803"; } -.icon-twitter:before { - content: "\e807"; + +.icon-twitter::before { + content: "\ea96"; } -.icon-down-open:before { + +.icon-down-open::before { content: "\e809"; } -.icon-trash:before { - content: "\e80c"; + +.icon-trash::before { + content: "\e9ac"; } -.icon-delete:before { - content: "\e600"; + +.icon-delete::before { + content: "\ea0d"; } -.icon-power:before { - content: "\e601"; + +.icon-power::before { + content: "\ea14"; } -.icon-arrow-up-thick:before { - content: "\e602"; + +.icon-arrow-up-thick::before { + content: "\ea3a"; } -.icon-rss:before { + +.icon-rss::before { content: "\e808"; } -.icon-print:before { - content: "\e80d"; + +.icon-print::before { + content: "\e954"; } -.icon-reload:before { + +.icon-reload::before { content: "\ea2e"; } +.icon-price-tags::before { + content: "\e936"; +} + +.icon-eye::before { + content: "\e9ce"; +} + +.icon-no-eye::before { + content: "\e9d1"; +} + +.icon-calendar::before { + content: "\e953"; +} + +.icon-time::before { + content: "\e952"; +} /* .icon-image class, for image-based icons ========================================================================== */ .icon-image { - background-size: 16px 16px; - background-repeat: no-repeat; - background-position: center; - padding-right: 1em !important; - padding-left: 1em !important; + background-size: 16px 16px; + background-repeat: no-repeat; + background-position: center; + padding-right: 1em !important; + padding-left: 1em !important; } /* Carrot (http://carrot.org) */ .icon-image--carrot { - background-image: url('../../_global/img/icons/carrot-icon--white.png'); + background-image: url("../../_global/img/icons/carrot-icon--white.png"); } /* Diaspora */ .icon-image--diaspora { - background-image: url('../../_global/img/icons/diaspora-icon--black.png'); + background-image: url("../../_global/img/icons/diaspora-icon--black.png"); +} + +/* shaarli */ +.icon-image--shaarli { + background-image: url("../../_global/img/icons/shaarli.png"); } /* ========================================================================== Icon selected ========================================================================== */ -.icon-star.fav:before { - color: #FFF; +.icon-star.fav::before { + color: #fff; } -.icon-check.archive:before { - color: #FFF; +.icon-check.archive::before { + color: #fff; } /* ========================================================================== @@ -797,33 +962,25 @@ a.add-to-wallabag-link-after:after { margin-top: 1em; } -.messages > * { display: inline-block;} +.messages > * { + display: inline-block; +} .warning { - /* font-size: 3em; - color: #999; - font-style: italic; - position: absolute; - top: 50%; - left: 0; - width: 100%; - text-align: center; - padding-right: 5%; - margin-top: -2em;*/ font-weight: bold; display: block; width: 100%; } .more-info { - font-size: 0.85em; + font-size: 0.85em; line-height: 1.5; - color: #aaa; + color: #aaa; } - .more-info a { +.more-info a { color: #aaa; - } +} /* ========================================================================== 5 = Article @@ -844,8 +1001,8 @@ a.add-to-wallabag-link-after:after { } blockquote { - border:1px solid #999; - background-color: #FFF; + border: 1px solid #999; + background-color: #fff; padding: 1em; margin: 0; } @@ -854,11 +1011,13 @@ blockquote { text-align: left; } -#article h2, #article h3, #article h4 { +#article h2, +#article h3, +#article h4 { text-transform: none; } -#article h2:after { +#article h2::after { content: none; } @@ -875,24 +1034,26 @@ blockquote { #article_toolbar li { display: inline-block; + margin: 3px auto; } #article_toolbar a { background-color: #000; padding: 0.3em 0.5em 0.2em; - color: #FFF; + color: #fff; text-decoration: none; } -#article_toolbar a:hover, #article_toolbar a:focus { +#article_toolbar a:hover, +#article_toolbar a:focus { background-color: #999; } #nav-btn-add-tag { - cursor: pointer; + cursor: pointer; } -.shaarli:before { +.shaarli::before { content: "*"; } @@ -902,7 +1063,7 @@ blockquote { display: block; } -.return:before { +.return::before { margin-right: 0.5em; } @@ -913,11 +1074,11 @@ blockquote { .icon-rss { background-color: #000; - color: #FFF; + color: #fff; padding: 0.2em 0.5em; } -.icon-rss:before { +.icon-rss::before { position: relative; top: 2px; } @@ -926,8 +1087,9 @@ blockquote { margin-bottom: 0.5em; } -.list-tags .icon-rss:hover, .list-tags .icon-rss:focus { - background-color: #FFF; +.list-tags .icon-rss:hover, +.list-tags .icon-rss:focus { + background-color: #fff; color: #000; text-decoration: none; } @@ -936,44 +1098,60 @@ blockquote { text-decoration: none; } -.list-tags a:hover, .list-tags a:focus { +.list-tags a:hover, +.list-tags a:focus { text-decoration: underline; } pre code { font-family: "Courier New", Courier, monospace; - border: 1px solid #ccc; - font-size: 0.96em; } -#filter-form { +#filters { position: fixed; width: 20%; height: 100%; top: 0; right: 0; - background-color: #FFF; + background-color: #fff; padding: 15px; padding-right: 30px; padding-top: 30px; border-left: 1px #333 solid; z-index: 12; - - /*transition-property: transform; - transition-duration: 0.3s; - transition-delay: 0.3s; - - transform: translate3d(100%, 0, 0);*/ + min-width: 300px; } -#filter-form form { - -} - -#filter-form form .filter-group { +#filters form .filter-group { margin: 5px; } +#download-form { + position: fixed; + width: 10%; + height: 100%; + top: 0; + right: 0; + background-color: #fff; + padding: 15px; + padding-right: 30px; + padding-top: 30px; + border-left: 1px #333 solid; + z-index: 12; + min-width: 200px; +} + +#download-form li { + display: block; + padding: 0.5em 2em 0.5em 1em; + color: #fff; + position: relative; + text-transform: uppercase; + text-decoration: none; + font-weight: 400; + font-family: PT Sans, sans-serif; + transition: all 0.5s ease; +} /* ========================================================================== 6 = Media Queries @@ -983,9 +1161,11 @@ pre code { .entry { width: 49%; } + .entry:nth-child(3n+1) { margin-left: 1.5%; } + .entry:nth-child(2n+1) { margin-left: 0; } @@ -995,6 +1175,7 @@ pre code { #article { width: 80%; } + .topPosF { right: 2.5em; } @@ -1005,16 +1186,26 @@ pre code { width: 100%; margin-left: 0; } + #display-mode { display: none; } } +@media screen and (max-height: 770px) { + .menu.users, + .menu.internal, + .menu.developer { + display: none; + } +} + @media screen and (max-width: 500px) { .entry { width: 100%; margin-left: 0; } + body > header { background-color: #333; position: fixed; @@ -1023,70 +1214,86 @@ pre code { height: 3em; z-index: 11; } + #links li:last-child { position: static; width: auto; } - #links li:last-child a:before { + + #links li:last-child a::before { content: none; } + .logo { width: 1.25em; height: 1.25em; left: 0; top: 0; } + .login > header { position: static; } + .login form { width: 100%; position: static; margin-left: 0; } + .login .logo { - width: auto; height: auto; top: 0.5em; width: 75px; - height: 75px; margin-left: -37.5px; } + .desktopHide { display: block; position: fixed; z-index: 20; top: 0; right: 0; - border:0; + border: 0; width: 2.5em; height: 2.5em; cursor: pointer; background-color: #999; font-size: 1.2em; } - .desktopHide:hover, .desktopHide:focus { - background-color: #FFF; - } + + .desktopHide:hover, + .desktopHide:focus { + background-color: #fff; + } + #links { display: none; width: 100%; height: auto; padding-top: 3em; } + #links.menu--open { display: block; } - footer { + + footer { position: static; margin-right: 3em; } + #main { margin-left: 1.5em; padding-right: 1.5em; position: static; margin-top: 3em; } + + .card-entry-labels { + display: none; + } + #article_toolbar .topPosF { display: none; } @@ -1098,6 +1305,7 @@ pre code { #article h1 { font-size: 1.5em; } + #article_toolbar a { padding: 0.3em 0.4em 0.2em; } @@ -1106,7 +1314,9 @@ pre code { display: none; } - .popup-form, #bagit-form, #search-form { + .popup-form, + #bagit-form, + #search-form { left: 0; width: 100%; border-left: none; diff --git a/app/Resources/static/themes/baggy/css/messages.css b/app/Resources/static/themes/baggy/css/messages.css new file mode 100755 index 000000000..bfaf14488 --- /dev/null +++ b/app/Resources/static/themes/baggy/css/messages.css @@ -0,0 +1,19 @@ +.messages.error.install { + border: 1px solid #c42608; + color: #c00 !important; + background: #fff0ef; + text-align: left; +} + +.messages.notice.install { + border: 1px solid #ebcd41; + color: #000; + background: #fffcd3; + text-align: left; +} + +.messages.success.install { + border: 1px solid #6dc70c; + background: #e0fbcc !important; + text-align: left; +} diff --git a/app/Resources/static/themes/baggy/css/print.css b/app/Resources/static/themes/baggy/css/print.css new file mode 100755 index 000000000..f7f6a8ad3 --- /dev/null +++ b/app/Resources/static/themes/baggy/css/print.css @@ -0,0 +1,66 @@ +@media print { + /* ### Layout ### */ + + body { + font-family: Serif; + background-color: #fff; + } + + @page { + margin: 1cm; + } + + img { + max-width: 100% !important; + } + + /* ### Content ### */ + + /* Hide useless blocks */ + body > header, + #article_toolbar, + #links, + #sort, + body > footer, + .top_link, + div.tools, + header div, + .messages, + .entrie + .results, + #article .mbm a, + #article-informations { + display: none !important; + } + + article { + border: none !important; + } + + /* Add URL after links */ + .vieworiginal a::after { + content: " (" attr(href) ")"; + } + + /* Add explanation after abbr */ + abbr[title]::after { + content: " (" attr(title) ")"; + } + + /* Change border on current pager item */ + .pagination span.current { + border-style: dashed; + } + + #main { + width: 100%; + padding: 0; + margin: 0; + margin-left: 0; + padding-right: 0; + padding-bottom: 0; + } + + #article { + width: 100%; + } +} diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/baggy/css/ratatouille.css b/app/Resources/static/themes/baggy/css/ratatouille.css similarity index 62% rename from src/Wallabag/CoreBundle/Resources/public/themes/baggy/css/ratatouille.css rename to app/Resources/static/themes/baggy/css/ratatouille.css index 605eb6411..a6167f344 100644 --- a/src/Wallabag/CoreBundle/Resources/public/themes/baggy/css/ratatouille.css +++ b/app/Resources/static/themes/baggy/css/ratatouille.css @@ -6,8 +6,8 @@ * { -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; } html { @@ -18,7 +18,7 @@ html { body { font-size: 1em; - line-height:1.5; + line-height: 1.5; margin: 0; } @@ -35,7 +35,7 @@ h6:first-child, p:first-child, ul:first-child, ol:first-child, -dl:first-child{ +dl:first-child { margin-top: 0; } @@ -43,14 +43,13 @@ code, kbd, pre, samp { - font-family: monospace, serif; + font-family: monospace, serif; } pre { - white-space: pre-wrap; + white-space: pre-wrap; } - .upper { text-transform: uppercase; } @@ -61,12 +60,14 @@ pre { .inner { margin: 0 auto; - max-width: 61.25em;/*980px*/ + max-width: 61.25em; /* 980px */ } -table, img, figure { +table, +img, +figure { max-width: 100%; - height :auto; + height: auto; } iframe { @@ -93,13 +94,13 @@ button, input, select, textarea { - font-family: inherit; - font-size: 100%; - margin: 0; + font-family: inherit; + font-size: 100%; + margin: 0; } input[type="search"] { - -webkit-appearance: textfield; + -webkit-appearance: textfield; } /* ========================================================================== @@ -115,11 +116,17 @@ input[type="search"] { display: none; } -.dtable { display:table } +.dtable { + display: table; +} - .dtable > * { display:table-row; } +.dtable > * { + display: table-row; +} - .dtable > * > * { display:table-cell; } +.dtable > * > * { + display: table-cell; +} .element-invisible { border: 0; @@ -133,32 +140,60 @@ input[type="search"] { } .small { - font-size:0.8em; + font-size: 0.8em; } .big { font-size: 1.2em; } -/*Width*/ +/* Width */ -.w100 { width:100%; } -.w90 { width:90%; } -.w80 { width:80%; } -.w70 { width:70%; } -.w60 { width:60%; } -.w50 { width:50%; } -.w40 { width:40%; } -.w30 { width:30%; } -.w20 { width:20%; } -.w10 { width:10%; } +.w100 { + width: 100%; +} +.w90 { + width: 90%; +} + +.w80 { + width: 80%; +} + +.w70 { + width: 70%; +} + +.w60 { + width: 60%; +} + +.w50 { + width: 50%; +} + +.w40 { + width: 40%; +} + +.w30 { + width: 30%; +} + +.w20 { + width: 20%; +} + +.w10 { + width: 10%; +} /* ========================================================================== Internet Explorer ========================================================================== */ -/*IE8 and IE9*/ +/* IE8 and IE9 */ article, aside, @@ -172,40 +207,20 @@ main, nav, section, summary { - display: block; + display: block; } -/*IE8 and IE9*/ +/* IE8 and IE9 */ audio, canvas, video { - display: inline-block; + display: inline-block; } -@media screen and (-webkit-min-device-pixel-ratio:0){ - select{ +@media screen and (-webkit-min-device-pixel-ratio: 0) { + select { -webkit-appearance: none; border-radius: 0; } } - -/* ========================================================================== - Medias Queries - ========================================================================== */ - -/*Desktop 1080px*/ - -@media screen and (max-width: 67.50em) { -} - -/*Tablet 800px*/ - -@media screen and (max-width: 50em) { -} - -/*Mobile 640px*/ - -@media screen and (max-width: 40em) { -} - diff --git a/app/Resources/static/themes/baggy/font/icomoon.eot b/app/Resources/static/themes/baggy/font/icomoon.eot new file mode 100644 index 000000000..248758fbd Binary files /dev/null and b/app/Resources/static/themes/baggy/font/icomoon.eot differ diff --git a/app/Resources/static/themes/baggy/font/icomoon.svg b/app/Resources/static/themes/baggy/font/icomoon.svg new file mode 100644 index 000000000..b1b9c92ac --- /dev/null +++ b/app/Resources/static/themes/baggy/font/icomoon.svg @@ -0,0 +1,501 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/Resources/static/themes/baggy/font/icomoon.ttf b/app/Resources/static/themes/baggy/font/icomoon.ttf new file mode 100644 index 000000000..c03770c6c Binary files /dev/null and b/app/Resources/static/themes/baggy/font/icomoon.ttf differ diff --git a/app/Resources/static/themes/baggy/font/icomoon.woff b/app/Resources/static/themes/baggy/font/icomoon.woff new file mode 100644 index 000000000..121e0bddb Binary files /dev/null and b/app/Resources/static/themes/baggy/font/icomoon.woff differ diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/baggy/img/baggy/blank.png b/app/Resources/static/themes/baggy/img/blank.png similarity index 100% rename from src/Wallabag/CoreBundle/Resources/public/themes/baggy/img/baggy/blank.png rename to app/Resources/static/themes/baggy/img/blank.png diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/baggy/img/baggy/down.png b/app/Resources/static/themes/baggy/img/down.png similarity index 100% rename from src/Wallabag/CoreBundle/Resources/public/themes/baggy/img/baggy/down.png rename to app/Resources/static/themes/baggy/img/down.png diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/baggy/img/baggy/list.png b/app/Resources/static/themes/baggy/img/list.png similarity index 100% rename from src/Wallabag/CoreBundle/Resources/public/themes/baggy/img/baggy/list.png rename to app/Resources/static/themes/baggy/img/list.png diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/baggy/img/baggy/table.png b/app/Resources/static/themes/baggy/img/table.png similarity index 100% rename from src/Wallabag/CoreBundle/Resources/public/themes/baggy/img/baggy/table.png rename to app/Resources/static/themes/baggy/img/table.png diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/baggy/img/baggy/top.png b/app/Resources/static/themes/baggy/img/top.png similarity index 100% rename from src/Wallabag/CoreBundle/Resources/public/themes/baggy/img/baggy/top.png rename to app/Resources/static/themes/baggy/img/top.png diff --git a/app/Resources/static/themes/baggy/js/autoCompleteTags.js b/app/Resources/static/themes/baggy/js/autoCompleteTags.js new file mode 100755 index 000000000..f287ebfa9 --- /dev/null +++ b/app/Resources/static/themes/baggy/js/autoCompleteTags.js @@ -0,0 +1,8 @@ +function split(val) { + return val.split(/,\s*/); +} +function extractLast(term) { + return split(term).pop(); +} + +export { split, extractLast }; diff --git a/app/Resources/static/themes/baggy/js/init.js b/app/Resources/static/themes/baggy/js/init.js new file mode 100755 index 000000000..dc11043a7 --- /dev/null +++ b/app/Resources/static/themes/baggy/js/init.js @@ -0,0 +1,314 @@ +import { savePercent, retrievePercent } from '../../_global/js/tools'; +import { toggleSaveLinkForm } from './uiTools'; + +const $ = global.jquery = require('jquery'); +require('jquery.cookie'); +require('jquery-ui-browserify'); +const annotator = require('annotator'); + + +$.fn.ready(() => { + const $listmode = $('#listmode'); + const $listentries = $('#list-entries'); + + /* ========================================================================== + Menu + ========================================================================== */ + + $('#menu').click(() => { + $('#links').toggleClass('menu--open'); + const content = $('#content'); + if (content.hasClass('opacity03')) { + content.removeClass('opacity03'); + } + }); + + /* ========================================================================== + List mode or Table Mode + ========================================================================== */ + + $listmode.click(() => { + if ($.cookie('listmode') === 1) { + // Cookie + $.removeCookie('listmode'); + + $listentries.removeClass('listmode'); + $listmode.removeClass('tablemode'); + $listmode.addClass('listmode'); + } else { + // Cookie + $.cookie('listmode', 1, { expires: 365 }); + + $listentries.addClass('listmode'); + $listmode.removeClass('listmode'); + $listmode.addClass('tablemode'); + } + }); + + /* ========================================================================== + Cookie listmode + ========================================================================== */ + + if ($.cookie('listmode') === 1) { + $listentries.addClass('listmode'); + $listmode.removeClass('listmode'); + $listmode.addClass('tablemode'); + } + + /* ========================================================================== + Add tag panel + ========================================================================== */ + + + $('#nav-btn-add-tag').on('click', () => { + $('.nav-panel-add-tag').toggle(100); + $('.nav-panel-menu').addClass('hidden'); + $('#tag_label').focus(); + return false; + }); + + /** + * Filters & Export + */ + // no display if filters not available + if ($('div').is('#filters')) { + $('#button_filters').show(); + $('#clear_form_filters').on('click', () => { + $('#filters input').val(''); + $('#filters :checked').removeAttr('checked'); + return false; + }); + } + + /* ========================================================================== + Annotations & Remember position + ========================================================================== */ + + if ($('article').length) { + const app = new annotator.App(); + + app.include(annotator.ui.main, { + element: document.querySelector('article'), + }); + + const x = JSON.parse($('#annotationroutes').html()); + app.include(annotator.storage.http, x); + + app.start().then(() => { + app.annotations.load({ entry: x.entryId }); + }); + + $(window).scroll(() => { + const scrollTop = $(window).scrollTop(); + const docHeight = $(document).height(); + const scrollPercent = (scrollTop) / (docHeight); + const scrollPercentRounded = Math.round(scrollPercent * 100) / 100; + savePercent(x.entryId, scrollPercentRounded); + }); + + retrievePercent(x.entryId); + + $(window).resize(() => { + retrievePercent(x.entryId); + }); + } + + /** + * Close window after adding entry if popup + */ + const currentUrl = window.location.href; + if (currentUrl.match('&closewin=true')) { + window.close(); + } + + /** + * Tags autocomplete + */ + /** + * Not working on v2 + * + + $('#value').bind('keydown', (event) => { + if (event.keyCode === $.ui.keyCode.TAB && $(this).data('ui-autocomplete').menu.active) { + event.preventDefault(); + } + }).autocomplete({ + source: function source(request, response) { + $.getJSON('./?view=tags', { + term: extractLast(request.term), + //id: $(':hidden#entry_id').val() + }, response); + }, + search: function search() { + // custom minLength + const term = extractLast(this.value); + return term.length >= 1; + }, + focus: function focus() { + // prevent value inserted on focus + return false; + }, + select: function select(event, ui) { + const terms = split(this.value); + // remove the current input + terms.pop(); + // add the selected item + terms.push(ui.item.value); + // add placeholder to get the comma-and-space at the end + terms.push(''); + this.value = terms.join(', '); + return false; + }, + }); + */ + + //--------------------------------------------------------------------------- + // Close the message box when the user clicks the close icon + //--------------------------------------------------------------------------- + $('a.closeMessage').on('click', () => { + $(this).parents('div.messages').slideUp(300, () => { $(this).remove(); }); + return false; + }); + + $('#search-form').hide(); + $('#bagit-form').hide(); + $('#filters').hide(); + $('#download-form').hide(); + + //--------------------------------------------------------------------------- + // Toggle the 'Search' popup in the sidebar + //--------------------------------------------------------------------------- + function toggleSearch() { + $('#search-form').toggle(); + $('#search').toggleClass('current'); + $('#search').toggleClass('active-current'); + $('#search-arrow').toggleClass('arrow-down'); + if ($('#search').hasClass('current')) { + $('#content').addClass('opacity03'); + } else { + $('#content').removeClass('opacity03'); + } + } + + //--------------------------------------------------------------------------- + // Toggle the 'Filter' popup on entries list + //--------------------------------------------------------------------------- + function toggleFilter() { + $('#filters').toggle(); + } + + //--------------------------------------------------------------------------- + // Toggle the 'Download' popup on entries list + //--------------------------------------------------------------------------- + function toggleDownload() { + $('#download-form').toggle(); + } + + //--------------------------------------------------------------------------- + // Toggle the 'Save a Link' popup in the sidebar + //--------------------------------------------------------------------------- + function toggleBagit() { + $('#bagit-form').toggle(); + $('#bagit').toggleClass('current'); + $('#bagit').toggleClass('active-current'); + $('#bagit-arrow').toggleClass('arrow-down'); + if ($('#bagit').hasClass('current')) { + $('#content').addClass('opacity03'); + } else { + $('#content').removeClass('opacity03'); + } + } + + //--------------------------------------------------------------------------- + // Close all #links popups in the sidebar + //--------------------------------------------------------------------------- + function closePopups() { + $('#links .messages').hide(); + $('#links > li > a').removeClass('active-current'); + $('#links > li > a').removeClass('current'); + $('[id$=-arrow]').removeClass('arrow-down'); + $('#content').removeClass('opacity03'); + } + + $('#search').click(() => { + closePopups(); + toggleSearch(); + $('#searchfield').focus(); + }); + + $('.filter-btn').click(() => { + closePopups(); + toggleFilter(); + }); + + $('.download-btn').click(() => { + closePopups(); + toggleDownload(); + }); + + $('#bagit').click(() => { + closePopups(); + toggleBagit(); + $('#plainurl').focus(); + }); + + $('#search-form-close').click(() => { + toggleSearch(); + }); + + $('#filter-form-close').click(() => { + toggleFilter(); + }); + + $('#download-form-close').click(() => { + toggleDownload(); + }); + + $('#bagit-form-close').click(() => { + toggleBagit(); + }); + + const $bagitFormForm = $('#bagit-form-form'); + + /* ========================================================================== + bag it link and close button + ========================================================================== */ + + // send 'bag it link' form request via ajax + $bagitFormForm.submit((event) => { + $('body').css('cursor', 'wait'); + $('#add-link-result').empty(); + + $.ajax({ + type: $bagitFormForm.attr('method'), + url: $bagitFormForm.attr('action'), + data: $bagitFormForm.serialize(), + success: function success() { + $('#add-link-result').html('Done!'); + $('#plainurl').val(''); + $('#plainurl').blur(''); + $('body').css('cursor', 'auto'); + }, + error: function error() { + $('#add-link-result').html('Failed!'); + $('body').css('cursor', 'auto'); + }, + }); + + event.preventDefault(); + }); + + /* ========================================================================== + Process all links inside an article + ========================================================================== */ + + $('article a[href^="http"]').after( + () => `' + ); + + $('.add-to-wallabag-link-after').click((event) => { + toggleSaveLinkForm($(this).attr('href'), event); + event.preventDefault(); + }); +}); diff --git a/app/Resources/static/themes/baggy/js/uiTools.js b/app/Resources/static/themes/baggy/js/uiTools.js new file mode 100644 index 000000000..900b27071 --- /dev/null +++ b/app/Resources/static/themes/baggy/js/uiTools.js @@ -0,0 +1,35 @@ +const $ = require('jquery'); + +function toggleSaveLinkForm(url, event) { + $('#add-link-result').empty(); + + const $bagit = $('#bagit'); + const $bagitForm = $('#bagit-form'); + + $bagit.toggleClass('active-current'); + + // only if bag-it link is not presented on page + if ($bagit.length === 0) { + if (event !== 'undefined' && event) { + $bagitForm.css({ position: 'absolute', top: event.pageY, left: event.pageX - 200 }); + } else { + $bagitForm.css({ position: 'relative', top: 'auto', left: 'auto' }); + } + } + + const searchForm = $('#search-form'); + const plainUrl = $('#plainurl'); + if (searchForm.length !== 0) { + $('#search').removeClass('current'); + $('#search-arrow').removeClass('arrow-down'); + searchForm.hide(); + } + $bagitForm.toggle(); + $('#content').toggleClass('opacity03'); + if (url !== 'undefined' && url) { + plainUrl.val(url); + } + plainUrl.focus(); +} + +export { toggleSaveLinkForm }; diff --git a/app/Resources/static/themes/material/css/main.css b/app/Resources/static/themes/material/css/main.css new file mode 100755 index 000000000..2cc2e8a0f --- /dev/null +++ b/app/Resources/static/themes/material/css/main.css @@ -0,0 +1,782 @@ +/* ========================================================================== + Sommaire + + 0 = Common + 1 = Nav + 2 = Side-nav + 3 = Filters slider + 4 = Cards + 5 = Article + 6 = Media queries + 7 = Font + 8 = Others + + ========================================================================== */ + +/* ========================================================================== + 0 = Common + ========================================================================== */ + +@font-face { + font-family: icomoon; + src: url("../fonts/IcoMoon-Free.ttf"); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: 'Material Icons'; + font-style: normal; + font-weight: 400; + src: url(../fonts/MaterialIcons-Regular.eot); + + /* For IE6-8 */ + src: local("Material Icons"), local("MaterialIcons-Regular"), url(../fonts/MaterialIcons-Regular.woff2) format("woff2"), url(../fonts/MaterialIcons-Regular.woff) format("woff"), url(../fonts/MaterialIcons-Regular.ttf) format("truetype"); +} + +.material-icons { + font-family: 'Material Icons'; + font-weight: normal; + font-style: normal; + font-size: 24px; /* Preferred icon size */ + width: 1em; + height: 1em; + display: inline-block; + line-height: 1; + text-transform: none; + letter-spacing: normal; + word-wrap: normal; + white-space: nowrap; + direction: ltr; + + /* Support for all WebKit browsers. */ + -webkit-font-smoothing: antialiased; + + /* Support for Safari and Chrome. */ + text-rendering: optimizeLegibility; + + /* Support for Firefox. */ + -moz-osx-font-smoothing: grayscale; + + /* Support for IE. */ + font-feature-settings: 'liga'; +} + +[class^="icon-"]::before, +[class*=" icon-"]::before { + font-family: icomoon; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + background-size: 24px; + + /* Enable Ligatures ================ */ + letter-spacing: 0; + -webkit-font-feature-settings: "liga"; + -moz-font-feature-settings: "liga=1"; + -moz-font-feature-settings: "liga"; + -ms-font-feature-settings: "liga" 1; + -o-font-feature-settings: "liga"; + font-feature-settings: "liga"; + + /* Better Font Rendering =========== */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-image { + background-size: 16px; + background-repeat: no-repeat; + padding-right: 1em !important; + padding-left: 1em !important; +} + +.icon-eye::before { + content: "\e9ce"; +} + +.icon-no-eye::before { + content: "\e9d1"; +} + +.icon-calendar::before { + content: "\e953"; +} + +.icon-mail::before { + content: "\ea86"; +} + +.icon-time::before { + content: "\e952"; +} + +/* Carrot (http://carrot.org) */ +.icon-image--carrot { + background-image: url("../../_global/img/icons/carrot-icon--black.png"); +} + +/* Diaspora */ +.icon-image--diaspora { + background-image: url("../../_global/img/icons/diaspora-icon--black.png"); +} + +/* Shaarli */ +.icon-image--shaarli { + background-image: url("../../_global/img/icons/shaarli.png"); +} + +body { + display: flex; + min-height: 100vh; + flex-direction: column; + background: #f0f0f0; +} + +body.login main { + padding: 0; + min-height: 100vh; +} + +.border-bottom { + border-bottom: 1px solid #ddd; +} + +nav, +main, +footer { + padding-left: 240px; +} + +main, +#content, +.valign-wrapper { + height: 100%; +} + +#main { + flex: 1 0 auto; +} + +.results { + height: 1em; + line-height: 30px; +} + +.results .nb-results, +.results .pagination { + margin: 15px; + margin-bottom: 0; +} + +.pagination { + float: right; +} + +.pagination ul { + margin: 0 !important; +} + +.pagination li { + padding: 0; +} + +.pagination a { + padding: 0 10px; + height: 30px; + display: block; +} + +.pagination .disabled { + margin-right: 10px; + margin-left: 10px; +} + +div.pagination ul .prev.disabled, +div.pagination ul .next.disabled { + display: none; +} + +.pagination li.active span { + padding: 0 10px; + height: 30px; + display: block; + color: #fff; +} + +.page-footer .footer-copyright p { + display: inline; +} + +.hidden { + display: none; +} + +.picker__date-display { + display: none; +} + +footer.page-footer { + margin-top: 10px; + padding-top: 0; +} + +footer .row { + margin-bottom: 10px; +} + +/* ========================================================================== + 1 = Nav + ========================================================================== */ + +nav input { + color: #aaa; +} + +.nav-wrapper .button-collapse { + padding: 0 15px; +} + +.nav-input { + display: none; +} + +.nav-panels { + overflow: hidden; +} + +.nav-panel-buttom li { + max-height: 64px; +} + +.nav-panels { + transition: background 0.2s ease; +} + +.nav-panel-add .add, +.nav-panel-search .search, +.nav-panels .close { + color: #444 !important; +} + +.nav-panels .action { + padding-left: 0.75rem; + font-size: 2.1rem; + white-space: nowrap; +} + +.nav-panels .input-field input { + display: block; + line-height: inherit; + padding-left: 4rem !important; + width: calc(100% - 8rem); +} + +.nav-panels .input-field input:focus { + background-color: #fff; + border: 0; + box-shadow: none; + color: #444; +} + +.input-field.nav-panel-add label { + left: 1rem; +} + +.input-field.nav-panel-add .close { + position: absolute; + top: 0; + right: 1rem; + color: transparent; + cursor: pointer; + font-size: 2rem; + transition: 0.3s color; +} + +#button_filters { + display: none; +} + +#button_export { + display: none; +} + +.input-field.nav-panel-add, +.input-field.nav-panel-add form { + height: 100%; +} + +/* ========================================================================== + 2 = Side-nav + ========================================================================== */ + +.side-nav.fixed a { + font-size: 13px; + line-height: 44px; + height: 44px; +} + +.side-nav .collapsible-header, +.side-nav.fixed .collapsible-header { + height: 45px; + line-height: 44px; + padding: 0 20px; +} + +.bold > a { + font-weight: bold; +} + +.side-nav > li.logo { + line-height: 0; + text-align: center; +} + +#main .logo a { + height: 100pt; +} + +#main .logo img { + height: 100pt; + width: 100pt; +} + +#main .logo:hover { + background: transparent; +} + +.side-nav li { + padding: 0; +} + +.side-nav a { + margin: 0 1rem; +} + +span.numberItems { + float: right; +} + +nav ul a:hover { + background-color: initial; +} + +/* ========================================================================== + * 3 = Filters slider + * ========================================================================== */ + +#filters button { + padding: 0; + width: 100%; +} + +.side-nav.fixed.right-aligned { + right: -250px; + left: auto !important; + overflow-y: visible; +} + +#filters div.with-checkbox { + height: 3rem; + margin-top: 0; +} + +/* ========================================================================== + 4 = Cards + ========================================================================== */ + +main #content { + padding: 0 0.5rem; +} + +main ul.row { + padding: 0 0.75rem; +} + +.data .card .card-body { + height: 22em; + overflow: hidden; +} + +.card .card-content .card-title { + line-height: 32px; + max-height: 64px; +} + +.card .card-content i.right, +.card .card-reveal i.right { + margin-left: 0; +} + +.card .card-entry-labels { + position: absolute; + top: 10px; + z-index: 90; + max-width: 50%; +} + +.card .card-entry-labels li, +.card-tag-labels li { + margin: 10px 10px 10px auto; + padding: 5px 12px 5px 16px !important; + background-color: rgba(0, 151, 167, 0.85); + border-radius: 0 3px 3px 0; + color: #fff; + cursor: default; + max-height: 2em; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.card .card-entry-labels-hidden { + margin-top: 5px; +} + +.card .card-entry-labels-hidden li { + display: inline-block; + background-color: rgba(0, 151, 167, 0.85); + margin: 0 5px; + padding: 5px 12px; + border-radius: 3px; + color: #fff; + max-height: 2em; + max-width: calc(100% - 15px); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.card-entry-tags a, +.card-entry-labels a, +.card-tag-labels a, +.card-entry-labels-hidden a, +#list .chip a { + text-decoration: none; + font-weight: normal; + color: #fff; +} + +.card .card-content .estimatedTime { + margin-bottom: 10px; +} + +.card .card-action .original { + line-height: 24px; +} + +.card .card-action ul.links { + margin: 0; + font-size: 24px; + line-height: 24px; +} + +.card .card-action a { + color: #fff; + margin: 0; +} + +.card .card-action a:hover { + color: #fff; +} + +.quickstart .card .card-action a, +.quickstart .card .card-action a:hover { + color: #fff !important; +} + +.settings .div_tabs { + padding-bottom: 15px; +} + +.card.sw { + max-width: 370px; + margin-left: auto; + margin-right: auto; +} + +.card .card-image { + height: 14em; +} + +.card .card-image .preview { + height: 14em; + background-size: cover; + background-repeat: no-repeat; + background-position: 50%; +} + +/* ========================================================================== + 5 = Article + ========================================================================== */ + +#article { + font-size: 20px; + margin: 0 auto; + max-width: 40em; +} + +#article img, +#article figure { + max-width: 100%; + height: auto; +} + +#article > header > h1 { + font-size: 1.6em; +} + +.reader-mode { + width: 95px !important; + transition: width 0.2s ease; +} + +.reader-mode:hover { + width: 240px !important; +} + +.reader-mode .collapsible-body { + height: 0; + overflow: hidden; +} + +.reader-mode:hover .collapsible-body { + height: auto; +} + +.reader-mode span { + opacity: 0; + transition: opacity 0.2s ease; +} + +.reader-mode:hover span { + opacity: 1; +} + +.progress { + position: fixed; + top: 0; + width: 100%; + height: 3px; + margin: 0; + z-index: 9999; +} + +#article aside .link { + color: #000; + font-size: 0.8em; + text-decoration: none; +} + +#article aside #list { + float: right; + margin: 0 15px 10px; +} + +#article aside .chip { + background-color: rgba(0, 151, 167, 0.85); + color: #fff; + padding: 0 15px 0 10px; +} + +#article aside .chip i { + color: #fff; +} + +/* ========================================================================== + 6 = Media queries + ========================================================================== */ + +@media only screen and (max-width: 992px) { + header, + main, + footer { + padding-left: 0; + } + + nav, + main, + footer { + padding-left: 0; + } + + .pagination { + width: auto; + } + + #article { + padding: 15px; + max-width: 35em; + margin-left: auto; + margin-right: auto; + font-size: 18px; + } + + #article > header > h1 { + font-size: 1.33em; + } + + .reader-mode { + width: 240px !important; + } + + .reader-mode span { + opacity: 1; + } + + .tabs { + display: inline-block; + height: auto; + } + + .tab { + min-width: 100%; + } + + .indicator { + display: none; + } + + .pagination li.prev, + .pagination li.next { + width: auto; + } +} + +@media only screen and (min-width: 400px) { + .nav-panel-buttom { + float: right; + } +} + +@media only screen and (min-width: 993px) and (max-width: 1180px) { + .row .col.l1 { + width: 25%; + margin-left: 0; + } + + .row .col.l2 { + width: 33.33333%; + margin-left: 0; + } + + .row .col.l3 { + width: 41.66667%; + margin-left: 0; + } + + .row .col.l4 { + width: 50%; + margin-left: 0; + } + + .row .col.l5 { + width: 58.33333%; + margin-left: 0; + } + + .row .col.l6 { + width: 66.66667%; + margin-left: 0; + } + + .row .col.l7 { + width: 75%; + margin-left: 0; + } + + .row .col.l8 { + width: 83.33333%; + margin-left: 0; + } + + .row .col.l9 { + width: 91.66667%; + margin-left: 0; + } + + .row .col.l10 { + width: 100%; + margin-left: 0; + } +} + +@media only screen and (max-width: 350px) { + .nb-results { + display: none; + } +} + +/* ========================================================================== + 7 = Font + ========================================================================== */ + +.icon-google-plus2::before { + content: "\ea89"; +} + +.icon-facebook2::before { + content: "\ea8d"; +} + +.icon-twitter::before { + content: "\ea96"; +} + +.icon-apple::before { + content: "\eabf"; +} + +.icon-android::before { + content: "\eac1"; +} + +.icon-chrome::before { + content: "\eae5"; +} + +.icon-firefox::before { + content: "\eae6"; +} + +.icon-link::before { + content: "\e9cb"; +} + +footer [class^="icon-"], +footer [class*=" icon-"] { + font-size: 2em; + transition: text-shadow 0.2s ease; + padding-right: 10px; +} + +footer [class^="icon-"]:hover, +footer [class*=" icon-"]:hover { + text-shadow: 0 0 10px rgba(0, 0, 0, 0.3); +} + +/* ========================================================================== + 8 = Others + ========================================================================== */ + +/* force height on non-input field in the settings page */ +div.settings div.input-field div, +div.settings div.input-field ul { + margin-top: 40px; +} + +/* but avoid to kill all file input */ +div.settings div.file-field div { + margin-top: inherit; +} + +.input-field label.active { + font-size: 1rem; +} + +nav .input-field input { + margin: 0; +} diff --git a/app/Resources/static/themes/material/css/print.css b/app/Resources/static/themes/material/css/print.css new file mode 100755 index 000000000..98505aae0 --- /dev/null +++ b/app/Resources/static/themes/material/css/print.css @@ -0,0 +1,77 @@ +@media print { + /* ### Layout ### */ + + body { + font-family: Serif; + background-color: #fff; + } + + @page { + margin: 1cm; + } + + img { + max-width: 100% !important; + } + + /* ### Content ### */ + + /* Hide useless blocks */ + body > header, + #article_toolbar, + #links, + #sort, + body > footer, + .top_link, + div.tools, + header div, + .messages, + .entry + .results, + #slide-out, + .progress, + .hide-on-large-only, + #article > aside, + #article .mbm a { + display: none !important; + } + + main { + padding-left: 0 !important; + } + + #article { + margin: inherit !important; + } + + article { + border: none !important; + } + + /* Add URL after links */ + .vieworiginal a::after { + content: " (" attr(href) ")"; + } + + /* Add explanation after abbr */ + abbr[title]::after { + content: " (" attr(title) ")"; + } + + /* Change border on current pager item */ + .pagination span.current { + border-style: dashed; + } + + #main { + width: 100%; + padding: 0; + margin: 0; + margin-left: 0; + padding-right: 0; + padding-bottom: 0; + } + + #article { + width: 100%; + } +} diff --git a/app/Resources/static/themes/material/js/init.js b/app/Resources/static/themes/material/js/init.js new file mode 100755 index 000000000..a68269e0b --- /dev/null +++ b/app/Resources/static/themes/material/js/init.js @@ -0,0 +1,99 @@ +import { savePercent, retrievePercent, initFilters, initExport } from '../../_global/js/tools'; + +const $ = require('jquery'); + +global.jQuery = $; +require('materialize'); // eslint-disable-line +const annotator = require('annotator'); + +$(document).ready(() => { + // sideNav + $('.button-collapse').sideNav(); + $('select').material_select(); + $('.collapsible').collapsible({ + accordion: false, + }); + $('.datepicker').pickadate({ + selectMonths: true, + selectYears: 15, + formatSubmit: 'dd/mm/yyyy', + hiddenName: true, + format: 'dd/mm/yyyy', + }); + initFilters(); + initExport(); + + $('#nav-btn-add-tag').on('click', () => { + $('.nav-panel-add-tag').toggle(100); + $('.nav-panel-menu').addClass('hidden'); + $('#tag_label').focus(); + return false; + }); + $('#nav-btn-add').on('click', () => { + $('.nav-panel-buttom').hide(100); + $('.nav-panel-add').show(100); + $('.nav-panels .action').hide(100); + $('.nav-panel-menu').addClass('hidden'); + $('.nav-panels').css('background', 'white'); + $('#entry_url').focus(); + return false; + }); + $('#nav-btn-search').on('click', () => { + $('.nav-panel-buttom').hide(100); + $('.nav-panel-search').show(100); + $('.nav-panels .action').hide(100); + $('.nav-panel-menu').addClass('hidden'); + $('.nav-panels').css('background', 'white'); + $('#searchfield').focus(); + return false; + }); + $('.close').on('click', () => { + $('.nav-panel-add').hide(100); + $('.nav-panel-search').hide(100); + $('.nav-panel-buttom').show(100); + $('.nav-panels .action').show(100); + $('.nav-panel-menu').removeClass('hidden'); + $('.nav-panels').css('background', 'transparent'); + return false; + }); + $(window).scroll(() => { + const s = $(window).scrollTop(); + const d = $(document).height(); + const c = $(window).height(); + const scrollPercent = (s / (d - c)) * 100; + $('.progress .determinate').css('width', `${scrollPercent}%`); + }); + +/* ========================================================================== + Annotations & Remember position + ========================================================================== */ + + if ($('article').length) { + const app = new annotator.App(); + const x = JSON.parse($('#annotationroutes').html()); + + app.include(annotator.ui.main, { + element: document.querySelector('article'), + }); + + app.include(annotator.storage.http, x); + + app.start().then(() => { + app.annotations.load({ entry: x.entryId }); + }); + + $(window).scroll(() => { + const scrollTop = $(window).scrollTop(); + const docHeight = $(document).height(); + const scrollPercent = (scrollTop) / (docHeight); + const scrollPercentRounded = Math.round(scrollPercent * 100) / 100; + savePercent(x.entryId, scrollPercentRounded); + }); + + retrievePercent(x.entryId); + + $(window).resize(() => { + retrievePercent(x.entryId); + }); + } +}); diff --git a/app/Resources/views/.gitkeep b/app/Resources/views/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/app/config/config.yml b/app/config/config.yml index 2b11937c7..5127c8cf7 100644 --- a/app/config/config.yml +++ b/app/config/config.yml @@ -30,7 +30,7 @@ framework: assets: ~ wallabag_core: - version: 2.0.8 + version: 2.1.3-dev paypal_url: "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9UBA65LG3FX9Y&lc=gb" languages: en: 'English' @@ -46,12 +46,16 @@ wallabag_core: it: 'Italiano' items_on_page: 12 theme: material - language: en + language: '%locale%' rss_limit: 50 reading_speed: 1 + cache_lifetime: 10 + +wallabag_user: + registration_enabled: "%fosuser_registration%" wallabag_import: - allow_mimetypes: ['application/octet-stream', 'application/json', 'text/plain'] + allow_mimetypes: ['application/octet-stream', 'application/json', 'text/plain', 'text/csv'] resource_dir: "%kernel.root_dir%/../web/uploads/import" # Twig Configuration @@ -60,19 +64,7 @@ twig: strict_variables: "%kernel.debug%" form_themes: - "LexikFormFilterBundle:Form:form_div_layout.html.twig" - -# Assetic Configuration -assetic: - debug: "%kernel.debug%" - use_controller: false - bundles: [ ] - #java: /usr/bin/java - filters: - cssrewrite: ~ - #closure: - # jar: "%kernel.root_dir%/Resources/java/compiler.jar" - #yui_css: - # jar: "%kernel.root_dir%/Resources/java/yuicompressor-2.4.7.jar" + exception_controller: wallabag_core.exception_controller:showAction # Doctrine Configuration doctrine: @@ -85,6 +77,7 @@ doctrine: password: "%database_password%" charset: UTF8 path: "%database_path%" + unix_socket: "%database_socket%" server_version: 5.6 orm: @@ -224,3 +217,106 @@ lexik_maintenance: response: code: 503 status: "wallabag Service Temporarily Unavailable" + +old_sound_rabbit_mq: + connections: + default: + host: "%rabbitmq_host%" + port: "%rabbitmq_port%" + user: "%rabbitmq_user%" + password: "%rabbitmq_password%" + vhost: / + lazy: true + producers: + import_pocket: + connection: default + exchange_options: + name: 'wallabag.import.pocket' + type: topic + import_readability: + connection: default + exchange_options: + name: 'wallabag.import.readability' + type: topic + import_instapaper: + connection: default + exchange_options: + name: 'wallabag.import.instapaper' + type: topic + import_wallabag_v1: + connection: default + exchange_options: + name: 'wallabag.import.wallabag_v1' + type: topic + import_wallabag_v2: + connection: default + exchange_options: + name: 'wallabag.import.wallabag_v2' + type: topic + import_firefox: + connection: default + exchange_options: + name: 'wallabag.import.firefox' + type: topic + import_chrome: + connection: default + exchange_options: + name: 'wallabag.import.chrome' + type: topic + consumers: + import_pocket: + connection: default + exchange_options: + name: 'wallabag.import.pocket' + type: topic + queue_options: + name: 'wallabag.import.pocket' + callback: wallabag_import.consumer.amqp.pocket + import_readability: + connection: default + exchange_options: + name: 'wallabag.import.readability' + type: topic + queue_options: + name: 'wallabag.import.readability' + callback: wallabag_import.consumer.amqp.readability + import_instapaper: + connection: default + exchange_options: + name: 'wallabag.import.instapaper' + type: topic + queue_options: + name: 'wallabag.import.instapaper' + callback: wallabag_import.consumer.amqp.instapaper + import_wallabag_v1: + connection: default + exchange_options: + name: 'wallabag.import.wallabag_v1' + type: topic + queue_options: + name: 'wallabag.import.wallabag_v1' + callback: wallabag_import.consumer.amqp.wallabag_v1 + import_wallabag_v2: + connection: default + exchange_options: + name: 'wallabag.import.wallabag_v2' + type: topic + queue_options: + name: 'wallabag.import.wallabag_v2' + callback: wallabag_import.consumer.amqp.wallabag_v2 + import_firefox: + connection: default + exchange_options: + name: 'wallabag.import.firefox' + type: topic + queue_options: + name: 'wallabag.import.firefox' + callback: wallabag_import.consumer.amqp.firefox + import_chrome: + connection: default + exchange_options: + name: 'wallabag.import.chrome' + type: topic + queue_options: + name: 'wallabag.import.chrome' + callback: wallabag_import.consumer.amqp.chrome diff --git a/app/config/config_dev.yml b/app/config/config_dev.yml index 0fa23dbfa..3b67d8f69 100644 --- a/app/config/config_dev.yml +++ b/app/config/config_dev.yml @@ -35,11 +35,16 @@ monolog: VERBOSITY_DEBUG: DEBUG channels: [doctrine] -assetic: - use_controller: true - swiftmailer: # see http://mailcatcher.me/ transport: smtp host: 'localhost' port: 1025 + +# If you want to use cache for queries used in WallabagExtension +# Uncomment the following lines +#doctrine: +# orm: +# metadata_cache_driver: apcu +# result_cache_driver: apcu +# query_cache_driver: apcu diff --git a/app/config/parameters.yml.dist b/app/config/parameters.yml.dist index d45839f4d..ece4903a1 100644 --- a/app/config/parameters.yml.dist +++ b/app/config/parameters.yml.dist @@ -18,6 +18,7 @@ parameters: database_password: ~ database_path: "%kernel.root_dir%/../data/db/wallabag.sqlite" database_table_prefix: wallabag_ + database_socket: null mailer_transport: smtp mailer_host: 127.0.0.1 @@ -34,6 +35,21 @@ parameters: twofactor_sender: no-reply@wallabag.org # fosuser stuff + fosuser_registration: true fosuser_confirmation: true from_email: no-reply@wallabag.org + + rss_limit: 50 + + # RabbitMQ processing + rabbitmq_host: localhost + rabbitmq_port: 5672 + rabbitmq_user: guest + rabbitmq_password: guest + + # Redis processing + redis_scheme: tcp + redis_host: localhost + redis_port: 6379 + redis_path: null diff --git a/app/config/parameters_test.yml b/app/config/parameters_test.yml index 696c95ef7..2943b27a7 100644 --- a/app/config/parameters_test.yml +++ b/app/config/parameters_test.yml @@ -5,4 +5,4 @@ parameters: test_database_name: null test_database_user: null test_database_password: null - test_database_path: '%kernel.root_dir%/../data/db/wallabag_testYO.sqlite' + test_database_path: '%kernel.root_dir%/../data/db/wallabag_test.sqlite' diff --git a/app/config/routing.yml b/app/config/routing.yml index 40cc7165b..750ed4356 100644 --- a/app/config/routing.yml +++ b/app/config/routing.yml @@ -7,6 +7,16 @@ wallabag_import: type: annotation prefix: /import +wallabag_user: + resource: "@WallabagUserBundle/Controller/" + type: annotation + prefix: /users + +wallabag_api: + resource: "@WallabagApiBundle/Controller/" + type: annotation + prefix: / + wallabag_api: resource: "@WallabagApiBundle/Resources/config/routing.yml" prefix: / diff --git a/app/config/security.yml b/app/config/security.yml index e24e03df2..efb00a534 100644 --- a/app/config/security.yml +++ b/app/config/security.yml @@ -60,6 +60,8 @@ security: - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY } - { path: /(unread|starred|archive).xml$, roles: IS_AUTHENTICATED_ANONYMOUSLY } + - { path: ^/share, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/settings, roles: ROLE_SUPER_ADMIN } - { path: ^/annotations, roles: ROLE_USER } + - { path: ^/users, roles: ROLE_SUPER_ADMIN } - { path: ^/, roles: ROLE_USER } diff --git a/app/config/services.yml b/app/config/services.yml index 480408d91..a57ef0f3d 100644 --- a/app/config/services.yml +++ b/app/config/services.yml @@ -16,6 +16,12 @@ services: wallabag.twig_extension: class: Wallabag\CoreBundle\Twig\WallabagExtension public: false + arguments: + - "@wallabag_core.entry_repository" + - "@wallabag_core.tag_repository" + - "@security.token_storage" + - "%wallabag_core.cache_lifetime%" + - "@translator" tags: - { name: twig.extension } diff --git a/app/config/tests/parameters_test.sqlite.yml b/app/config/tests/parameters_test.sqlite.yml index 6e9f98de5..1952e3a61 100644 --- a/app/config/tests/parameters_test.sqlite.yml +++ b/app/config/tests/parameters_test.sqlite.yml @@ -5,4 +5,4 @@ parameters: test_database_name: ~ test_database_user: ~ test_database_password: ~ - test_database_path: "%kernel.root_dir%/../data/db/wallabag_testHU.sqlite" + test_database_path: "%kernel.root_dir%/../data/db/wallabag_test.sqlite" diff --git a/bin/symfony_requirements b/bin/symfony_requirements index 8825a9646..a7bf65a1b 100755 --- a/bin/symfony_requirements +++ b/bin/symfony_requirements @@ -22,7 +22,6 @@ echo '> Checking Symfony requirements:'.PHP_EOL.' '; $messages = array(); foreach ($symfonyRequirements->getRequirements() as $req) { - /** @var $req Requirement */ if ($helpText = get_error_message($req, $lineSize)) { echo_style('red', 'E'); $messages['error'][] = $helpText; @@ -121,10 +120,14 @@ function echo_block($style, $title, $message) echo PHP_EOL.PHP_EOL; - echo_style($style, str_repeat(' ', $width).PHP_EOL); - echo_style($style, str_pad(' ['.$title.']', $width, ' ', STR_PAD_RIGHT).PHP_EOL); - echo_style($style, str_pad($message, $width, ' ', STR_PAD_RIGHT).PHP_EOL); - echo_style($style, str_repeat(' ', $width).PHP_EOL); + echo_style($style, str_repeat(' ', $width)); + echo PHP_EOL; + echo_style($style, str_pad(' ['.$title.']', $width, ' ', STR_PAD_RIGHT)); + echo PHP_EOL; + echo_style($style, $message); + echo PHP_EOL; + echo_style($style, str_repeat(' ', $width)); + echo PHP_EOL; } function has_color_support() diff --git a/composer.json b/composer.json index c7830a148..79de337ba 100644 --- a/composer.json +++ b/composer.json @@ -48,7 +48,6 @@ "doctrine/doctrine-bundle": "^1.6", "doctrine/doctrine-cache-bundle": "^1.2", "twig/extensions": "~1.0", - "symfony/assetic-bundle": "~2.3", "symfony/swiftmailer-bundle": "^2.3", "symfony/monolog-bundle": "^2.8", "sensio/distribution-bundle": "^5.0", @@ -64,12 +63,11 @@ "willdurand/hateoas-bundle": "~1.0", "htmlawed/htmlawed": "~1.1.19", "liip/theme-bundle": "~1.1", - "pagerfanta/pagerfanta": "~1.0.3", "lexik/form-filter-bundle": "~5.0", "j0k3r/graby": "~1.0", - "friendsofsymfony/user-bundle": "~2.0@dev", + "friendsofsymfony/user-bundle": "dev-master#e168ed64629d034cb9cbbffb9d4350f62ef04fab as 2.0.x-dev", "friendsofsymfony/oauth-server-bundle": "^1.5", - "stof/doctrine-extensions-bundle": "^1.2@dev", + "stof/doctrine-extensions-bundle": "^1.2", "scheb/two-factor-bundle": "~2.0", "grandt/phpepub": "~4.0", "wallabag/php-mobi": "~1.0.0", @@ -81,24 +79,26 @@ "mnapoli/piwik-twig-extension": "^1.0", "lexik/maintenance-bundle": "~2.1", "ocramius/proxy-manager": "1.*", - "white-october/pagerfanta-bundle": "^1.0" + "white-october/pagerfanta-bundle": "^1.0", + "php-amqplib/rabbitmq-bundle": "^1.8", + "predis/predis": "^1.0", + "javibravo/simpleue": "^1.0" }, "require-dev": { "doctrine/doctrine-fixtures-bundle": "~2.2", "doctrine/data-fixtures": "~1.1.1", "sensio/generator-bundle": "^3.0", - "phpunit/phpunit": "~4.4", + "phpunit/phpunit": "~5.0", "symfony/phpunit-bridge": "^3.0", - "friendsofphp/php-cs-fixer": "~1.9" + "friendsofphp/php-cs-fixer": "~1.9", + "m6web/redis-mock": "^2.0" }, "scripts": { "post-cmd": [ "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters", "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap", "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache", - "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets", - "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile", - "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::prepareDeploymentTarget" + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile" ], "post-install-cmd": [ "@post-cmd" @@ -113,7 +113,6 @@ "symfony-var-dir": "var", "symfony-web-dir": "web", "symfony-tests-dir": "tests", - "symfony-assets-install": "relative", "incenteev-parameters": { "file": "app/config/parameters.yml" } diff --git a/docker-compose.yml b/docker-compose.yml index 50d8c546e..3c28f2f1a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,6 +11,7 @@ services: links: - php:php command: nginx -c /nginx.conf + php: build: context: docker/php @@ -30,6 +31,7 @@ services: # If all DBMS are commented out, sqlite will be used as default # - ./docker/postgres/env # - ./docker/mariadb/env + #postgres: # image: postgres:9 # ports: @@ -38,6 +40,7 @@ services: # - ./docker/data/pgsql:/var/lib/postgresql/data # env_file: # - ./docker/postgres/env + #mariadb: # image: mariadb:10 # ports: @@ -46,3 +49,13 @@ services: # - ./docker/data/mariadb:/var/lib/mysql # env_file: # - ./docker/mariadb/env + + rabbitmq: + image: rabbitmq:3-management + ports: + - "15672:15672" + + redis: + image: redis + ports: + - "6379:6379" diff --git a/docs/de/conf.py b/docs/de/conf.py index 8f2d130d0..cc9dcdf81 100644 --- a/docs/de/conf.py +++ b/docs/de/conf.py @@ -12,7 +12,7 @@ source_suffix = '.rst' master_doc = 'index' project = u'wallabag-fr' copyright = u'2013-2016, Nicolas Lœuillet - MIT Licence' -version = '2.0.0' +version = '2.1.0' release = version exclude_patterns = ['_build'] pygments_style = 'sphinx' diff --git a/docs/de/developer/maintenance.rst b/docs/de/developer/maintenance.rst index 2a9070e17..31343876a 100644 --- a/docs/de/developer/maintenance.rst +++ b/docs/de/developer/maintenance.rst @@ -11,7 +11,7 @@ Um den Wartungsmodus zu aktivieren, führe folgendes Kommando aus: :: - bin/console lexik:maintenance:lock --no-interaction + bin/console lexik:maintenance:lock -e=prod --no-interaction Du kannst deine IP Adresse in ``app/config/config.yml`` setzen, wenn du Zugriff zu wallabag haben willst, auch wenn der Wartungsmodus aktiv ist. Zum Beispiel: @@ -29,4 +29,4 @@ Um den Wartungsmodus zu deaktivieren, führe dieses Kommando aus: :: - bin/console lexik:maintenance:unlock + bin/console lexik:maintenance:unlock -e=prod diff --git a/docs/de/developer/rabbitmq.rst b/docs/de/developer/rabbitmq.rst new file mode 100644 index 000000000..f81e07e3e --- /dev/null +++ b/docs/de/developer/rabbitmq.rst @@ -0,0 +1,81 @@ +Installiere RabbitMQ für asynchrone Aufgaben +============================================ + +Um asynchrone Aufgaben zu starten (nützlich z.B. für große Imports), können wir RabbitMQ nutzen. + +Voraussetzungen +--------------- + +Du musst RabbitMQ auf deinem Server installiert haben. + +Installation +~~~~~~~~~~~~ + +.. code:: bash + + wget https://www.rabbitmq.com/rabbitmq-signing-key-public.asc + apt-key add rabbitmq-signing-key-public.asc + apt-get update + apt-get install rabbitmq-server + +Konfiguration und Starten +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: bash + + rabbitmq-plugins enable rabbitmq_management # (useful to have a web interface, available at http://localhost:15672/ (guest/guest) + rabbitmq-server -detached + +RabbitMQ stoppen +~~~~~~~~~~~~~~~ + +.. code:: bash + + rabbitmqctl stop + + +Konfigure RabbitMQ in wallabag +------------------------------ + +Bearbeite die Datei ``parameters.yml``, um die RabbitMQ Konfiguration einzurichten. Die Standardkonfiguration sollte ok sein: + +.. code:: yaml + + rabbitmq_host: localhost + rabbitmq_port: 5672 + rabbitmq_user: guest + rabbitmq_password: guest + +Enable RabbitMQ in wallabag +--------------------------- + +In internal settings, in the **Import** section, enable RabbitMQ (with the value 1). + +Starte den RabbitMQ Consumer +---------------------------- + +Abhängig von welchem Service du importieren möchtest, solltest du einen Cron Job aktivieren (oder mehrere, wenn du viele unterstützen willst): + +.. code:: bash + + # for Pocket import + bin/console rabbitmq:consumer -e=prod import_pocket -w + + # for Readability import + bin/console rabbitmq:consumer -e=prod import_readability -w + + # for Instapaper import + bin/console rabbitmq:consumer -e=prod import_instapaper -w + + # for wallabag v1 import + bin/console rabbitmq:consumer -e=prod import_wallabag_v1 -w + + # for wallabag v2 import + bin/console rabbitmq:consumer -e=prod import_wallabag_v2 -w + + # for Firefox import + bin/console rabbitmq:consumer -e=prod import_firefox -w + + # for Chrome import + bin/console rabbitmq:consumer -e=prod import_chrome -w + diff --git a/docs/de/developer/redis.rst b/docs/de/developer/redis.rst new file mode 100644 index 000000000..57b415506 --- /dev/null +++ b/docs/de/developer/redis.rst @@ -0,0 +1,75 @@ +Installiere Redis für asynchrone Aufgaben +========================================= + +Um asynchrone Aufgaben zu starten (nützlich z.B. für große Imports), können wir Redis nutzen. + +Voraussetzungen +--------------- + +Du musst Redis auf deinem Server installiert haben. + +Installation +~~~~~~~~~~~~ + +.. code:: bash + + apt-get install redis-server + +Starten +~~~~~~ + +Der Redis Service läuft eventuell schon direkt nach der Installation. Falls nicht kannst du ihn wie folgt starten: + +.. code:: bash + + redis-server + + +Konfigure Redis in wallabag +--------------------------- + +Bearbeite die Datei ``parameters.yml``, um die RabbitMQ Konfiguration einzurichten. Die Standardkonfiguration sollte ok sein: + +.. code:: yaml + + redis_host: localhost + redis_port: 6379 + +Enable Redis in wallabag +------------------------ + +In internal settings, in the **Import** section, enable Redis (with the value 1). + +Starte den Redis Consumer +------------------------- + +Abhängig von welchem Service du importieren möchtest, solltest du einen Cron Job aktivieren (oder mehrere, wenn du viele unterstützen willst): + +.. code:: bash + + # for Pocket import + bin/console wallabag:import:redis-worker -e=prod pocket -vv >> /path/to/wallabag/var/logs/redis-pocket.log + + # for Readability import + bin/console wallabag:import:redis-worker -e=prod readability -vv >> /path/to/wallabag/var/logs/redis-readability.log + + # for Instapaper import + bin/console wallabag:import:redis-worker -e=prod instapaper -vv >> /path/to/wallabag/var/logs/redis-instapaper.log + + # for wallabag v1 import + bin/console wallabag:import:redis-worker -e=prod wallabag_v1 -vv >> /path/to/wallabag/var/logs/redis-wallabag_v1.log + + # for wallabag v2 import + bin/console wallabag:import:redis-worker -e=prod wallabag_v2 -vv >> /path/to/wallabag/var/logs/redis-wallabag_v2.log + + # for Firefox import + bin/console wallabag:import:redis-worker -e=prod firefox -vv >> /path/to/wallabag/var/logs/redis-firefox.log + + # for Chrome import + bin/console wallabag:import:redis-worker -e=prod instapaper -vv >> /path/to/wallabag/var/logs/redis-chrome.log + +Wenn du den Import nur für ein paar Nachrichten und nicht für alle starten willst, kannst du die Nummer (im folgenden Beispiel 12) angeben. Der Redis Worker wird dann nach der 12. Nachricht stoppen: + +.. code:: bash + + bin/console wallabag:import:redis-worker -e=prod pocket -vv --maxIterations=12 diff --git a/docs/de/developer/testsuite.rst b/docs/de/developer/testsuite.rst new file mode 100644 index 000000000..b2b16cdc0 --- /dev/null +++ b/docs/de/developer/testsuite.rst @@ -0,0 +1,10 @@ +Testsuite +========= + +To ensure wallabag development quality, we wrote tests with `PHPUnit `_. + +If you contribute to the project (by translating the application, by fixing bugs or by adding a new feature), please write your own tests. + +To launch wallabag testsuite, you need to install `ant `_. + +Then, execute this command ``make test``. diff --git a/docs/de/index.rst b/docs/de/index.rst index 5311150fa..a380d7bdd 100644 --- a/docs/de/index.rst +++ b/docs/de/index.rst @@ -17,6 +17,11 @@ Die Hauptdokumentation für diese Applikation ist in einigen Abschnitten organis * :ref:`user-docs` * :ref:`dev-docs` +Die Dokumentation ist in anderen Sprachen verfügbar : + +* `Documentation in english `_ +* `Documentation en français `_ + .. _user-docs: .. toctree:: @@ -25,7 +30,8 @@ Die Hauptdokumentation für diese Applikation ist in einigen Abschnitten organis user/faq user/installation - user/upgrade + user/upgrade-2.0.x-2.1.1 + user/upgrade-2.0.x-2.1.1 user/migration user/import user/create_account @@ -38,6 +44,7 @@ Die Hauptdokumentation für diese Applikation ist in einigen Abschnitten organis user/filters user/tags user/android + user/parameters .. _dev-docs: @@ -50,3 +57,5 @@ Die Hauptdokumentation für diese Applikation ist in einigen Abschnitten organis developer/documentation developer/translate developer/maintenance + developer/redis + developer/rabbitmq diff --git a/docs/de/user/configuration.rst b/docs/de/user/configuration.rst index 67695ff10..0b0793b11 100644 --- a/docs/de/user/configuration.rst +++ b/docs/de/user/configuration.rst @@ -102,7 +102,7 @@ Wenn *readingTime >= 5 AND domainName = "github.com"*, dann tagge als *lange zu Welche Variablen und Operatoren kann ich zum Regeln schreiben nutzen? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Die folgenden Variabel und Operatoren können genutzt werden, um Tagging-Regeln zu erstellen: +Die folgenden Variablen und Operatoren können genutzt werden, um Tagging-Regeln zu erstellen (sei vorsichtig, denn bei einigen Werten musst du Anführungszeichen hinzufügen, z.B. ``language = "de"``): =========== ============================================== ======== ========== Variable Bedeutung Operator Bedeutung diff --git a/docs/de/user/installation.rst b/docs/de/user/installation.rst index fced2ed4a..35a30f524 100644 --- a/docs/de/user/installation.rst +++ b/docs/de/user/installation.rst @@ -6,6 +6,20 @@ Voraussetzungen wallabag ist kompatibel mit PHP >= 5.5, inkl. PHP 7. +.. note:: + + To install wallabag easily, we create a ``Makefile``, so you need to have the ``make`` tool. + +wallabag nutzt eine große Anzahl an Bibliotheken, um zu funktionieren. Diese Bibliotheken müssen mit einem Tool namens Composer installiert werden. Du musst es installieren sofern du es bisher noch nicht gemacht hast. + +Composer installieren: + +:: + + curl -s http://getcomposer.org/installer | php + +Du kannst eine spezifische Anleitung `hier `__ finden. + Du benötigst die folgenden Extensions damit wallabag funktioniert. Einige von diesen sind vielleicht schon in deiner Version von PHP aktiviert, somit musst du eventuell nicht alle folgenden Pakete installieren. @@ -23,6 +37,7 @@ nicht alle folgenden Pakete installieren. - php-curl - php-gettext - php-tokenizer +- php-bcmath wallabag nutzt PDO, um sich mit der Datenbank zu verbinden, darum benötigst du eines der folgenden Komponenten: @@ -38,33 +53,20 @@ Installation Auf einem dedizierten Webserver (empfohlener Weg) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -wallabag nutzt eine große Anzahl an Bibliotheken, um zu funktionieren. Diese Bibliotheken müssen mit einem Tool namens Composer installiert werden. Du musst es installieren sofern du es bisher noch nicht gemacht hast. - -Composer installieren: - -:: - - curl -s http://getcomposer.org/installer | php - -Du kannst eine spezifische Anleitung `hier `__ finden: - Um wallabag selbst zu installieren, musst du die folgenden Kommandos ausführen: :: git clone https://github.com/wallabag/wallabag.git - cd wallabag - git checkout 2.0.8 - SYMFONY_ENV=prod composer install --no-dev -o --prefer-dist - php bin/console wallabag:install --env=prod + cd wallabag && make install Um PHPs eingebauten Server zu starten und zu testen, ob alles korrekt installiert wurde, kannst du folgendes Kommando ausführen: :: - php bin/console server:run --env=prod + make run -Und wallabag unter http://deineserverip:8000 erreichen +und wallabag unter http://deineserverip:8000 erreichen. .. tip:: @@ -86,18 +88,18 @@ Führe dieses Kommando aus, um das neueste Paket herunterzuladen und zu entpacke wget http://wllbg.org/latest-v2-package && tar xvf latest-v2-package -(md5 hash: ``4f84c725d1d6e3345eae0a406115e5ff``) +Du findest die `md5 Hashsumme des neuesten Pakets auf unserer Website `_. -Jetzt lese die Dokumentation, um einen Virtualhost zu erstellen, dann greife auf dein wallabag zu. +Jetzt lies die Dokumentation, um einen Virtualhost zu erstellen, dann greife auf dein wallabag zu. Wenn du die Datenbankkonfiguration eingestellt hast, MySQL oder PostgreSQL zu nutzen, musst du einen Nutzer über das folgende Kommando erstellen ``php bin/console wallabag:install --env=prod``. Installation mit Docker ------------------------- +~~~~~~~~~~~~~~~~~~~~~~~ Wir stellen ein Docker Image zu Verfügung, um wallabag einfach zu installieren. Schaue in unser Repository in unserem `Docker Hub `__, um mehr Informationen zu erhalten. Kommando, um den Container zu starten -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: bash @@ -107,7 +109,7 @@ Virtualhosts ------------ Konfiguration von Apache -~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~ Angenommen du willst wallabag in das Verzeichnis ``/var/www/wallabag`` installieren und du willst PHP als Apache Modul nutzen, dann ist hier ein vhost für wallabag: @@ -189,15 +191,14 @@ Angenommen du willst wallabag in das Verzeichnis ``/var/www/wallabag`` installie access_log /var/log/nginx/wallabag_access.log; } -Nach dem neuladen oder neustarten von nginx, solltest du nun wallabag unter http://domain.tld erreichen. +Nach dem Neuladen oder Neustarten von nginx solltest du nun wallabag unter http://domain.tld erreichen. .. tip:: - When you want to import large file into wallabag, you need to add this line in your nginx configuration ``client_max_body_size XM; # allows file uploads up to X megabytes``. Wenn du eine große Datei in wallabag importieren willst, solltest du diese Zeile zu deiner nginx Konfiguration hinzufügen ``client_max_body_size XM; # allows file uploads up to X megabytes``. Konfiguration von lighttpd -~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~ Angenommen du willst wallabag in das Verzeichnis ``/var/www/wallabag`` installieren, dann ist hier ein Rezept für wallabag (bearbeite deine ``lighttpd.conf`` und füge die Konfiguration dort ein): diff --git a/docs/de/user/parameters.rst b/docs/de/user/parameters.rst new file mode 100644 index 000000000..4c68fe9e6 --- /dev/null +++ b/docs/de/user/parameters.rst @@ -0,0 +1,50 @@ +Was bedeuten die Parameter? +=========================== +.. csv-table:: Datenbankparameter + :header: "Name", "Standardwert", "Beschreibung" + + "database_driver", "pdo_sqlite", "Sollte pdo_sqlite oder pdo_mysql oder pdo_pgsql sein" + "database_host", "127.0.0.1", "Hostadresse deiner Datenbank (normalerweise localhost oder 127.0.0.1)" + "database_port", "~", "Port deiner Datenbank (Du kannst ``~`` stehen lassen, um den Standardport zu nutzen)" + "database_name", "symfony", "Benenne deine Datenbank" + "database_user", "root", "Benutzer, der Schreibrecht in der Datenbank hat" + "database_password", "~", "Passwort des Benutzers" + "database_path", "``""%kernel.root_dir%/../data/db/wallabag.sqlite""``", "nur für SQLite, definiere, wo die Datenbankdatei abgelegt werden soll. Lass den Parameter leer für andere Datenbanktypen." + "database_table_prefix", "wallabag_", "alle wallabag Tabellen erhalten diesen Präfix im Namen. Du kannst einen ``_`` dafür im Präfix nutzen, um das zu verdeutlichen." + "database_socket", "null", "Wenn deine Datenbank einen Socket statt TCP nutzt, schreibe hier den Pfad zum Socket hin (andere Verbindungsparameter werden dann ignoriert." + +.. csv-table:: Konfiguration, um mit wallabag E-Mails senden zu können + :header: "Name", "Standardwert", "Beschreibung" + + "mailer_transport", "smtp", "Die exakte Transportmethode, um E-Mails zuzustellen. Gültige Werte sind: smtp, gmail, mail, sendmail, null (was das Mailen deaktivert)" + "mailer_host", "127.0.0.1", "Der Host, zu dem sich verbunden wird, wenn SMTP als Transport genutzt wird." + "mailer_user", "~", "Der Benutzername, wenn SMTP als Transport genutzt wird." + "mailer_password", "~", "Das Passwort, wenn SMTP als Transport genutzt wird." + +.. csv-table:: Andere wallabag Optionen + :header: "Name", "Standardwert", "Beschreibung" + + "locale", "en", "Standardsprache deiner wallabag Instanz (wie z.B. en, fr, es, etc.)" + "secret", "ovmpmAWXRCabNlMgzlzFXDYmCFfzGv", "Dieser String sollte einzigartig für deine Applikation sein und er wird genutzt, um sicherheitsrelevanten Operationen mehr Entropie hinzuzufügen." + "twofactor_auth", "true", "true, um Zwei-Faktor-Authentifizierung zu aktivieren" + "twofactor_sender", "no-reply@wallabag.org", "E-Mail-Adresse des Senders der Mails mit dem Code für die Zwei-Faktor-Authentifizierung" + "fosuser_registration", "true", "true, um die Registrierung für jedermann zu aktivieren" + "fosuser_confirmation", "true", "true, um eine Bestätigungsmail für jede Registrierung zu senden" + "from_email", "no-reply@wallabag.org", "E-Mail-Adresse, die im Absenderfeld jeder Mail genutzt wird" + "rss_limit", "50", "Artikellimit für RSS Feeds" + +.. csv-table:: RabbitMQ Konfiguration + :header: "Name", "Standardwert", "Beschreibung" + + "rabbitmq_host", "localhost", "Host deines RabbitMQ" + "rabbitmq_port", "5672", "Port deines RabbitMQ" + "rabbitmq_user", "guest", "Benutzer, der die Queue lesen kann" + "rabbitmq_password", "guest", "Passwort dieses Benutzers" + +.. csv-table:: Redis Konfiguration + :header: "Name", "Standardwert", "Beschreibung" + + "redis_scheme", "tcp", "Bestimmt das Protokoll, dass genutzt wird, um mit Redis zu kommunizieren. Gültige Werte sind: tcp, unix, http" + "redis_host", "localhost", "IP oder Hostname des Zielservers (ignoriert bei Unix Schema)" + "redis_port", "6379", "TCP/IP Port des Zielservers (ignoriert bei Unix Schema)" + "redis_path", "null", "Pfad zur Unix Domain Socket Datei, wenn Redis Unix Domain Sockets nutzt" diff --git a/docs/de/user/upgrade-2.0.x-2.1.1.rst b/docs/de/user/upgrade-2.0.x-2.1.1.rst new file mode 100644 index 000000000..db81a1bec --- /dev/null +++ b/docs/de/user/upgrade-2.0.x-2.1.1.rst @@ -0,0 +1,85 @@ +Wallabag von 2.0.x auf 2.1.1 updaten +==================================== + +.. warning:: +Wenn du den Import von Pocket durch das Hinzufügen des Consumer Key in den internen Einstellungen konfiguriert hast, fertige bitte ein Backup deines Keys an, bevor du auf das neue Release migrierst: Du wirst den Key nach dem Update in der Konfiguration erneut eintragen müssen. + +Update auf einem dedizierten Webserver +-------------------------------------- + +Das neueste Release ist auf https://www.wallabag.org/pages/download-wallabag.html veröffentlicht. Um deine wallabag-Installation auf die neueste Version zu aktualisieren, führe die folgenden Kommandos in deinem wallabag-Ordner aus (ersetze ``2.1.1`` mit der neuesten Releasenummer): + +:: + + rm -rf var/cache/* + git fetch origin + git fetch --tags + git checkout 2.1.1 --force + SYMFONY_ENV=prod composer install --no-dev -o --prefer-dist + php bin/console doctrine:migrations:migrate --env=prod + php bin/console cache:clear --env=prod + +Update auf einem Shared Webhosting +---------------------------------- + +Sichere deine ``app/config/parameters.yml`` Datei. + +Lade das neueste Release von wallabag herunter: + +.. code-block:: bash + + wget http://wllbg.org/latest-v2-package && tar xvf latest-v2-package + +(2.1.1 md5 Hashsumme: ``9584a3b60a2b2a4de87f536548caac93``) + +Entpacke das Archiv in deinen wallabag-Ordner und ersetze ``app/config/parameters.yml`` mit deiner Datei. + +Bitte beachte, dass wir in dieser Version neue Parameter hinzugefügt haben. Du musst die Datei ``app/config/parameters.yml`` bearbeiten und die folgenden Zeilen hinzufügen (ersetze die Werte mit deiner Konfiguration): + +.. code-block:: yml + + parameters: + database_driver: pdo_sqlite + database_host: 127.0.0.1 + database_port: null + database_name: symfony + database_user: root + database_password: null + database_path: '%kernel.root_dir%/../data/db/wallabag.sqlite' + database_table_prefix: wallabag_ + mailer_transport: smtp + mailer_host: 127.0.0.1 + mailer_user: null + mailer_password: null + locale: en + secret: ovmpmAWXRCabNlMgzlzFXDYmCFfzGv + twofactor_auth: true + twofactor_sender: no-reply@wallabag.org + fosuser_registration: true + fosuser_confirmation: true + from_email: no-reply@wallabag.org + rss_limit: 50 + rabbitmq_host: localhost + rabbitmq_port: 5672 + rabbitmq_user: guest + rabbitmq_password: guest + redis_host: localhost + redis_port: 6379 + +Du kannst `hier eine Dokumentation über die Parameter finden `_. + +Wenn du SQLite nutzt, musst auch das ``data/`` Verzeichnis in die neue Installation kopieren. + +Leere den ``var/cache`` Ordner. + +Du musst einige SQL-Abfragen ausführen, um die Datenbank zu aktualisieren. Wir nehmen in diesem Fall an, dass das Tabellenpräfix ``wallabag_`` ist und eine MySQL-Datenbank genutzt wird: + +.. code-block:: sql + + ALTER TABLE `wallabag_entry` ADD `uuid` LONGTEXT DEFAULT NULL; + INSERT INTO `wallabag_craue_config_setting` (`name`, `value`, `section`) VALUES ('share_public', '1', 'entry'); + ALTER TABLE `wallabag_oauth2_clients` ADD name longtext COLLATE 'utf8_unicode_ci' DEFAULT NULL; + INSERT INTO `wallabag_craue_config_setting` (`name`, `value`, `section`) VALUES ('import_with_redis', '0', 'import'); + INSERT INTO `wallabag_craue_config_setting` (`name`, `value`, `section`) VALUES ('import_with_rabbitmq', '0', 'import'); + ALTER TABLE `wallabag_config` ADD `pocket_consumer_key` VARCHAR(255) DEFAULT NULL; + DELETE FROM `wallabag_craue_config_setting` WHERE `name` = 'pocket_consumer_key'; diff --git a/docs/de/user/upgrade.rst b/docs/de/user/upgrade-2.1.x-2.1.y.rst similarity index 74% rename from docs/de/user/upgrade.rst rename to docs/de/user/upgrade-2.1.x-2.1.y.rst index c04b68f34..2f9b7791b 100644 --- a/docs/de/user/upgrade.rst +++ b/docs/de/user/upgrade-2.1.x-2.1.y.rst @@ -1,16 +1,17 @@ -Wallabag updaten -================ +Wallabag von 2.1.x auf 2.1.y updaten +==================================== Update auf einem dedizierten Webserver -------------------------------------- -Das neueste Release ist auf https://www.wallabag.org/pages/download-wallabag.html veröffentlicht. Um deine wallabag Installation auf die neueste Version upzudaten, führe die folgenden Kommandos in deinem wallabag Ordner aus (ersetze ``2.0.8`` mit der neuesten Releasenummer): +Das neueste Release ist auf https://www.wallabag.org/pages/download-wallabag.html veröffentlicht. Um deine wallabag Installation auf die neueste Version upzudaten, führe die folgenden Kommandos in deinem wallabag Ordner aus (ersetze ``2.1.2`` mit der neuesten Releasenummer): :: + rm -rf var/cache/* git fetch origin git fetch --tags - git checkout 2.0.8 + git checkout 2.1.2 --force SYMFONY_ENV=prod composer install --no-dev -o --prefer-dist php bin/console cache:clear --env=prod @@ -25,7 +26,7 @@ Lade das neueste Release von wallabag herunter: wget http://wllbg.org/latest-v2-package && tar xvf latest-v2-package -(md5 hash: ``4f84c725d1d6e3345eae0a406115e5ff``) +Du findest die `md5 Hashsumme des neuesten Pakets auf unserer Website `_. Entpacke das Archiv in deinen wallabag Ordner und ersetze ``app/config/parameters.yml`` mit deiner Datei. diff --git a/docs/en/conf.py b/docs/en/conf.py index 86e337040..717b35f14 100644 --- a/docs/en/conf.py +++ b/docs/en/conf.py @@ -12,7 +12,7 @@ source_suffix = '.rst' master_doc = 'index' project = u'wallabag' copyright = u'2013-2016, Nicolas Lœuillet - MIT Licence' -version = '2.0.0' +version = '2.1.0' release = version exclude_patterns = ['_build'] pygments_style = 'sphinx' diff --git a/docs/en/developer/api.rst b/docs/en/developer/api.rst index 83f0bb8f3..4828cddd1 100644 --- a/docs/en/developer/api.rst +++ b/docs/en/developer/api.rst @@ -8,7 +8,7 @@ Requirements * wallabag freshly (or not) installed on http://localhost:8000 * ``httpie`` installed on your computer (`see project website `__). Note that you can also adapt the commands using curl or wget. -* all the API methods are documented here http://localhost:8000/api/doc +* all the API methods are documented here http://localhost:8000/api/doc (on your instance) and `on our example instance `_ Creating a new API client ------------------------- diff --git a/docs/en/developer/maintenance.rst b/docs/en/developer/maintenance.rst index 8277fb1f8..6d55ed601 100644 --- a/docs/en/developer/maintenance.rst +++ b/docs/en/developer/maintenance.rst @@ -11,7 +11,7 @@ To enable maintenance mode, execute this command: :: - bin/console lexik:maintenance:lock --no-interaction + bin/console lexik:maintenance:lock --no-interaction -e=prod You can set your IP address in ``app/config/config.yml`` if you want to access to wallabag even if maintenance mode is enabled. For example: @@ -29,4 +29,4 @@ To disable maintenance mode, execute this command: :: - bin/console lexik:maintenance:unlock + bin/console lexik:maintenance:unlock -e=prod diff --git a/docs/en/developer/rabbitmq.rst b/docs/en/developer/rabbitmq.rst new file mode 100644 index 000000000..673228e9f --- /dev/null +++ b/docs/en/developer/rabbitmq.rst @@ -0,0 +1,80 @@ +Install RabbitMQ for asynchronous tasks +======================================= + +In order to launch asynchronous tasks (useful for huge imports for example), we can use RabbitMQ. + +Requirements +------------ + +You need to have RabbitMQ installed on your server. + +Installation +~~~~~~~~~~~~ + +.. code:: bash + + wget https://www.rabbitmq.com/rabbitmq-signing-key-public.asc + apt-key add rabbitmq-signing-key-public.asc + apt-get update + apt-get install rabbitmq-server + +Configuration and launch +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: bash + + rabbitmq-plugins enable rabbitmq_management # (useful to have a web interface, available at http://localhost:15672/ (guest/guest) + rabbitmq-server -detached + +Stop RabbitMQ +~~~~~~~~~~~~~ + +.. code:: bash + + rabbitmqctl stop + + +Configure RabbitMQ in wallabag +------------------------------ + +Edit your ``parameters.yml`` file to edit RabbitMQ configuration. The default one should be ok: + +.. code:: yaml + + rabbitmq_host: localhost + rabbitmq_port: 5672 + rabbitmq_user: guest + rabbitmq_password: guest + +Enable RabbitMQ in wallabag +--------------------------- + +In internal settings, in the **Import** section, enable RabbitMQ (with the value 1). + +Launch RabbitMQ consumer +------------------------ + +Depending on which service you want to import from you need to enable one (or many if you want to support many) cron job: + +.. code:: bash + + # for Pocket import + bin/console rabbitmq:consumer -e=prod import_pocket -w + + # for Readability import + bin/console rabbitmq:consumer -e=prod import_readability -w + + # for Instapaper import + bin/console rabbitmq:consumer -e=prod import_instapaper -w + + # for wallabag v1 import + bin/console rabbitmq:consumer -e=prod import_wallabag_v1 -w + + # for wallabag v2 import + bin/console rabbitmq:consumer -e=prod import_wallabag_v2 -w + + # for Firefox import + bin/console rabbitmq:consumer -e=prod import_firefox -w + + # for Chrome import + bin/console rabbitmq:consumer -e=prod import_chrome -w diff --git a/docs/en/developer/redis.rst b/docs/en/developer/redis.rst new file mode 100644 index 000000000..2e2bbbeaf --- /dev/null +++ b/docs/en/developer/redis.rst @@ -0,0 +1,75 @@ +Install Redis for asynchronous tasks +==================================== + +In order to launch asynchronous tasks (useful for huge imports for example), we can use Redis. + +Requirements +------------ + +You need to have Redis installed on your server. + +Installation +~~~~~~~~~~~~ + +.. code:: bash + + apt-get install redis-server + +Launch +~~~~~~ + +The server might be already running after installing, if not you can launch it using: + +.. code:: bash + + redis-server + + +Configure Redis in wallabag +--------------------------- + +Edit your ``parameters.yml`` file to edit Redis configuration. The default one should be ok: + +.. code:: yaml + + redis_host: localhost + redis_port: 6379 + +Enable Redis in wallabag +------------------------ + +In internal settings, in the **Import** section, enable Redis (with the value 1). + +Launch Redis consumer +--------------------- + +Depending on which service you want to import from you need to enable one (or many if you want to support many) cron job: + +.. code:: bash + + # for Pocket import + bin/console wallabag:import:redis-worker -e=prod pocket -vv >> /path/to/wallabag/var/logs/redis-pocket.log + + # for Readability import + bin/console wallabag:import:redis-worker -e=prod readability -vv >> /path/to/wallabag/var/logs/redis-readability.log + + # for Instapaper import + bin/console wallabag:import:redis-worker -e=prod instapaper -vv >> /path/to/wallabag/var/logs/redis-instapaper.log + + # for wallabag v1 import + bin/console wallabag:import:redis-worker -e=prod wallabag_v1 -vv >> /path/to/wallabag/var/logs/redis-wallabag_v1.log + + # for wallabag v2 import + bin/console wallabag:import:redis-worker -e=prod wallabag_v2 -vv >> /path/to/wallabag/var/logs/redis-wallabag_v2.log + + # for Firefox import + bin/console wallabag:import:redis-worker -e=prod firefox -vv >> /path/to/wallabag/var/logs/redis-firefox.log + + # for Chrome import + bin/console wallabag:import:redis-worker -e=prod instapaper -vv >> /path/to/wallabag/var/logs/redis-chrome.log + +If you want to launch the import only for some messages and not all, you can specify this number (here 12) and the worker will stop right after the 12th message : + +.. code:: bash + + bin/console wallabag:import:redis-worker -e=prod pocket -vv --maxIterations=12 diff --git a/docs/en/developer/testsuite.rst b/docs/en/developer/testsuite.rst new file mode 100644 index 000000000..b2b16cdc0 --- /dev/null +++ b/docs/en/developer/testsuite.rst @@ -0,0 +1,10 @@ +Testsuite +========= + +To ensure wallabag development quality, we wrote tests with `PHPUnit `_. + +If you contribute to the project (by translating the application, by fixing bugs or by adding a new feature), please write your own tests. + +To launch wallabag testsuite, you need to install `ant `_. + +Then, execute this command ``make test``. diff --git a/docs/en/index.rst b/docs/en/index.rst index 03025ef92..6d85db2ba 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -17,6 +17,11 @@ The main documentation for this application is organized into a couple sections: * :ref:`user-docs` * :ref:`dev-docs` +The documentation is available in other languages: + +* `Documentation en français `_ +* `Deutsch Dokumentation `_ + .. _user-docs: .. toctree:: @@ -25,7 +30,8 @@ The main documentation for this application is organized into a couple sections: user/faq user/installation - user/upgrade + user/upgrade-2.0.x-2.1.1 + user/upgrade-2.1.x-2.1.y user/migration user/import user/create_account @@ -35,9 +41,11 @@ The main documentation for this application is organized into a couple sections: user/errors_during_fetching user/annotations user/download_articles + user/share user/filters user/tags user/android + user/parameters .. _dev-docs: @@ -50,3 +58,5 @@ The main documentation for this application is organized into a couple sections: developer/documentation developer/translate developer/maintenance + developer/redis + developer/rabbitmq diff --git a/docs/en/user/android.rst b/docs/en/user/android.rst index 4271f0373..ccbad2642 100644 --- a/docs/en/user/android.rst +++ b/docs/en/user/android.rst @@ -29,13 +29,13 @@ Fill in your wallabag data. You need to enter your wallabag address. It is impor :alt: Filled in settings :align: center -After you have filled in your data, push the button Connection test and wait for the test to finish. +After you have filled in your data, push the button Connection test and wait for the test to finish. .. image:: ../../img/user/android_configuration_connection_test.en.png :alt: Connection test with your wallabag data :align: center -The connection test shall finish with success. If not, you need to fix this first until you proceed. +The connection test should finish with success. If not, you need to fix this first until you proceed. .. image:: ../../img/user/android_configuration_connection_test_success.en.png :alt: Connection test successful @@ -65,16 +65,16 @@ After hitting the save button, you get the following screen. The app proposes to :alt: Settings saved the first time :align: center -Finally after the syncronisation finished successfully, you are presented the list of unread articles. +Finally after the synchronisation finished successfully, you are presented to the list of unread articles. .. image:: ../../img/user/android_unread_feed_synced.en.png - :alt: Filled article list cause feeds successfully syncronized + :alt: Filled article list cause feeds successfully synchronized :align: center Known limitations ----- +----------------- 2FA ~~~ @@ -85,7 +85,7 @@ Currently the does not support two-factor authentication. You should disable tha Limited amount of articles with wallabag v2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In your wallabag web instance you can configure how many items are part of the RSS feed. This option did not exist in wallabag v1, where all articles were part of the feed. So if you set the amount of articles being displayed greater than the number of items being content of your RSS feed, you will only see the number of items in your RSS feed. +In your wallabag web instance you can configure how many items are part of the RSS feed. This option did not exist in wallabag v1, where all articles were part of the feed. So if you set the amount of articles being displayed greater than the number of items being content of your RSS feed, you will only see the number of items in your RSS feed. SSL/TLS encryption diff --git a/docs/en/user/configuration.rst b/docs/en/user/configuration.rst index f4c55dea6..f74924dfe 100644 --- a/docs/en/user/configuration.rst +++ b/docs/en/user/configuration.rst @@ -100,7 +100,7 @@ if *« readingTime >= 5 AND domainName = "github.com" »* then tag as *« long r Which variables and operators can I use to write rules? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The following variables and operators can be used to create tagging rules: +The following variables and operators can be used to create tagging rules (be careful, for some values, you need to add quotes, for example ``language = "en"``): =========== ============================================== ======== ========== Variable Meaning Operator Meaning diff --git a/docs/en/user/errors_during_fetching.rst b/docs/en/user/errors_during_fetching.rst index 688927503..6684563ad 100644 --- a/docs/en/user/errors_during_fetching.rst +++ b/docs/en/user/errors_during_fetching.rst @@ -12,9 +12,21 @@ There may be several reasons: How can I help to fix that? --------------------------- -- `by sending us an email with the article's URL `_ -- by trying to fix this article by yourself :) by creating a file for the article. - You can use `this tool `__. +You can `sending us an email with the article's URL `_. + +Or you can also try to fix this problem by yourself (so we can be focused on improving wallabag internally instead of writing siteconfig :) ). + +You can try to see if it works here: `http://f43.me/feed/test `_ (it uses almost the same system as wallabag to retrieve content). + +If it works here and not on wallabag, it means there is something internally in wallabag that breaks the parser (hard to fix: please open an issue about it). + +If it doesn't works, try to extract a site config using: `http://siteconfig.fivefilters.org/ `_ (select which part of the content is actually the content). You can `read this documentation before `_. + +You can test it on **f43.me** website: click on **Want to try a custom siteconfig?** and put the generated file from siteconfig.fivefilters.org. + +Repeat until you have something ok. + +Then you can submit a pull request to `https://github.com/fivefilters/ftr-site-config `_ which is the global repo for siteconfig files. How can I try to re-fetch this article? --------------------------------------- diff --git a/docs/en/user/import.rst b/docs/en/user/import.rst index 632104847..70f0187d4 100644 --- a/docs/en/user/import.rst +++ b/docs/en/user/import.rst @@ -23,22 +23,43 @@ Now, all is fine to migrate from Pocket. Import your data into wallabag 2.x ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Click on ``Import`` link in the menu, on ``Import contents`` in Pocket section +Click on ``Import`` link in the menu, on ``Import contents`` in Pocket section and then on ``Connect to Pocket and import data``. You need to authorize wallabag to interact with your Pocket account. -Your data will be imported. Data import can be a demanding process for your server -(we need to work on this import to improve it). - -From Instapaper ---------------- - -*Feature not yet implemented in wallabag v2.* +Your data will be imported. Data import can be a demanding process for your server. From Readability ---------------- -*Feature not yet implemented in wallabag v2.* +Export your Readability data +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +On the tools (`https://www.readability.com/tools/ `_) page, click on "Export your data" in the "Data Export" section. You will received an email to download a json (which does not end with .json in fact). + +Import your data into wallabag 2.x +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Click on ``Import`` link in the menu, on ``Import contents`` in Readability section +and then select your json file and upload it. + +Your data will be imported. Data import can be a demanding process for your server. + +From Instapaper +--------------- + +Export your Instapaper data +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +On the settings (`https://www.instapaper.com/user `_) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like ``instapaper-export.csv``). + +Import your data into wallabag 2.x +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Click on ``Import`` link in the menu, on ``Import contents`` in Instapaper section +and then select your CSV file and upload it. + +Your data will be imported. Data import can be a demanding process for your server. From HTML or JSON file ---------------------- diff --git a/docs/en/user/installation.rst b/docs/en/user/installation.rst index 763d7c66d..45e146164 100644 --- a/docs/en/user/installation.rst +++ b/docs/en/user/installation.rst @@ -6,7 +6,21 @@ Requirements wallabag is compatible with PHP >= 5.5, including PHP 7. -You'll need the following extensions for wallabag to work. Some of these may already activated in your version of PHP, so you may not have to install all corresponding packages. +.. note:: + + To install wallabag easily, we provide a ``Makefile``, so you need to have the ``make`` tool. + +wallabag uses a large number of PHP libraries in order to function. These libraries must be installed with a tool called Composer. You need to install it if you have not already done so and be sure to use the 1.2 version (if you already have Composer, run a ``composer selfupdate``). + +Install Composer: + +:: + + curl -s http://getcomposer.org/installer | php + +You can find specific instructions `here `__. + +You'll also need the following extensions for wallabag to work. Some of these may already activated in your version of PHP, so you may not have to install all corresponding packages. - php-session - php-ctype @@ -22,6 +36,7 @@ You'll need the following extensions for wallabag to work. Some of these may alr - php-curl - php-gettext - php-tokenizer +- php-bcmath wallabag uses PDO to connect to the database, so you'll need one of the following: @@ -37,31 +52,18 @@ Installation On a dedicated web server (recommended way) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -wallabag uses a large number of libraries in order to function. These libraries must be installed with a tool called Composer. You need to install it if you have not already done so and be sure to use the 1.2 version (if you already have Composer, run a ``composer selfupdate``). - -Install Composer: - -:: - - curl -s http://getcomposer.org/installer | php - -You can find specific instructions `here `__: - To install wallabag itself, you must run the following commands: :: git clone https://github.com/wallabag/wallabag.git - cd wallabag - git checkout 2.0.8 - SYMFONY_ENV=prod composer install --no-dev -o --prefer-dist - php bin/console wallabag:install --env=prod + cd wallabag && make install To start PHP's build-in server and test if everything did install correctly, you can do: :: - php bin/console server:run --env=prod + make run And access wallabag at http://yourserverip:8000 @@ -85,18 +87,18 @@ Execute this command to download and extract the latest package: wget http://wllbg.org/latest-v2-package && tar xvf latest-v2-package -(md5 hash of the package: ``4f84c725d1d6e3345eae0a406115e5ff``) +You will find the `md5 hash of the latest package on our website `_. Now, read the following documentation to create your virtual host, then access your wallabag. If you changed the database configuration to use MySQL or PostgreSQL, you need to create a user via this command ``php bin/console wallabag:install --env=prod``. Installation with Docker ------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~ -We provide you a Docker image to install wallabag easily. Have a look to our repository on `Docker Hub `__ to have more information. +We provide you a Docker image to install wallabag easily. Have a look at our repository on `Docker Hub `__ for more information. Command to launch container -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: bash @@ -192,12 +194,12 @@ After reloading or restarting nginx, you should now be able to access wallabag a .. tip:: - When you want to import large file into wallabag, you need to add this line in your nginx configuration ``client_max_body_size XM; # allows file uploads up to X megabytes``. + When you want to import large files into wallabag, you need to add this line in your nginx configuration ``client_max_body_size XM; # allows file uploads up to X megabytes``. Configuration on lighttpd ~~~~~~~~~~~~~~~~~~~~~~~~~ -Assuming you install wallabag in the /var/www/wallabag folder, here's the recipe for wallabag (edit your ``lighttpd.conf`` file and paste this configuration into it): +Assuming you install wallabag in the ``/var/www/wallabag`` folder, here's the recipe for wallabag (edit your ``lighttpd.conf`` file and paste this configuration into it): :: @@ -238,16 +240,16 @@ Rights access to the folders of the project Test environment ~~~~~~~~~~~~~~~~ -When we just want to test wallabag, we just run the command ``php bin/console server:run --env=prod`` to start our wallabag instance and everything will go smoothly because the user who started the project can access to the current folder naturally, without any problem. +When we just want to test wallabag, we just run the command ``make run`` to start our wallabag instance and everything will go smoothly because the user who started the project can access to the current folder naturally, without any problem. Production environment ~~~~~~~~~~~~~~~~~~~~~~ -As soon as we use Apache or Nginx to access to our wallabag instance, and not from the command ``php bin/console server:run --env=prod`` to start it, we should take care to grant the good rights on the good folders to keep safe all the folders of the project. +As soon as we use Apache or Nginx to access to our wallabag instance, and not from the command ``make run`` to start it, we should take care to grant the good rights on the good folders to keep safe all the folders of the project. To do so, the folder name, known as ``DocumentRoot`` (for apache) or ``root`` (for Nginx), has to be absolutely accessible by the Apache/Nginx user. Its name is generally ``www-data``, ``apache`` or ``nobody`` (depending on linux system used). -So the folder ``/var/www/wallabag/web`` has to be accessible by this last one. But this could be not enough if we just care about this folder, because we could meet a blank page or get an error 500 when trying to access to the homepage of the project. +So the folder ``/var/www/wallabag/web`` has to be accessible by this last one. But this may not be enough if we just care about this folder, because we could meet a blank page or get an error 500 when trying to access to the homepage of the project. This is due to the fact that we will need to grant the same rights access on the folder ``/var/www/wallabag/var`` like those we gave on the folder ``/var/www/wallabag/web``. Thus, we fix this problem with the following command: diff --git a/docs/en/user/migration.rst b/docs/en/user/migration.rst index e141ae40d..420627969 100644 --- a/docs/en/user/migration.rst +++ b/docs/en/user/migration.rst @@ -24,29 +24,6 @@ After creating an user account on your new wallabag v2 instance, you must head o :alt: Import from wallabag v1 :align: center -Import via command-line interface (CLI) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you have a CLI access on your web server, you can execute this command to import your wallabag v1 export: - -:: - - bin/console wallabag:import-v1 1 ~/Downloads/wallabag-export-1-2016-04-05.json --env=prod - -Please replace values: - -* ``1`` is the user identifier in database (The ID of the first user created on wallabag is 1) -* ``~/Downloads/wallabag-export-1-2016-04-05.json`` is the path of your wallabag v1 export - -You'll have this in return: - -:: - - Start : 05-04-2016 11:36:07 --- - 403 imported - 0 already saved - End : 05-04-2016 11:36:09 --- - From wallabag 2.x ----------------- @@ -60,3 +37,30 @@ From your new wallabag instance, create your user account and click on the link .. note:: If you encounter issues during the export or the import, don't hesitate to `ask for support `__. + +Import via command-line interface (CLI) +--------------------------------------- + +If you have a CLI access on your web server, you can execute this command to import your wallabag v1 export: + +:: + + bin/console wallabag:import 1 ~/Downloads/wallabag-export-1-2016-04-05.json --env=prod + +Please replace values: + +* ``1`` is the user identifier in database (The ID of the first user created on wallabag is 1) +* ``~/Downloads/wallabag-export-1-2016-04-05.json`` is the path of your wallabag v1 export + +If you want to mark all these entries as read, you can add the ``--markAsRead`` option. + +To import a wallabag v2 file, you need to add the option ``--importer=v2``. + +You'll have this in return: + +:: + + Start : 05-04-2016 11:36:07 --- + 403 imported + 0 already saved + End : 05-04-2016 11:36:09 --- diff --git a/docs/en/user/parameters.rst b/docs/en/user/parameters.rst new file mode 100644 index 000000000..6cbd5ae4b --- /dev/null +++ b/docs/en/user/parameters.rst @@ -0,0 +1,50 @@ +What is the meaning of the parameters? +====================================== +.. csv-table:: Database parameters + :header: "name", "default", "description" + + "database_driver", "pdo_sqlite", "Should be pdo_sqlite or pdo_mysql or pdo_pgsql" + "database_host", "127.0.0.1", "host of your database (usually localhost or 127.0.0.1)" + "database_port", "~", "port of your database (you can leave ``~`` to use the default one)" + "database_name", "symfony", "name of your database" + "database_user", "root", "user that can write to this database" + "database_password", "~", "password of that user" + "database_path", "``""%kernel.root_dir%/../data/db/wallabag.sqlite""``", "only for SQLite, define where to put the database file. Leave it empty for other database" + "database_table_prefix", "wallabag_", "all wallabag's tables will be prefixed with that string. You can include a ``_`` for clarity" + "database_socket", "null", "If your database is using a socket instead of tcp, put the path of the socket (other connection parameters will then be ignored)" + +.. csv-table:: Configuration to send emails from wallabag + :header: "name", "default", "description" + + "mailer_transport", "smtp", "The exact transport method to use to deliver emails. Valid values are: smtp, gmail, mail, sendmail, null (which will disable the mailer)" + "mailer_host", "127.0.0.1", "The host to connect to when using smtp as the transport." + "mailer_user", "~", "The username when using smtp as the transport." + "mailer_password", "~", "The password when using smtp as the transport." + +.. csv-table:: Other wallabag's option + :header: "name", "default", "description" + + "locale", "en", "Default language of your wallabag instance (like en, fr, es, etc.)" + "secret", "ovmpmAWXRCabNlMgzlzFXDYmCFfzGv", "This is a string that should be unique to your application and it's commonly used to add more entropy to security related operations." + "twofactor_auth", "true", "true to enable Two factor authentication" + "twofactor_sender", "no-reply@wallabag.org", "email of the email sender to receive the two factor code" + "fosuser_registration", "true", "true to enable public registration" + "fosuser_confirmation", "true", "true to send a confirmation by email for each registration" + "from_email", "no-reply@wallabag.org", "email address used in From: field in each email" + "rss_limit", "50", "limit for RSS feeds" + +.. csv-table:: RabbitMQ configuration + :header: "name", "default", "description" + + "rabbitmq_host", "localhost", "Host of your RabbitMQ" + "rabbitmq_port", "5672", "Port of your RabbitMQ" + "rabbitmq_user", "guest", "User that can read queues" + "rabbitmq_password", "guest", "Password of that user" + +.. csv-table:: Redis configuration + :header: "name", "default", "description" + + "redis_scheme", "tcp", "Specifies the protocol used to communicate with an instance of Redis. Valid values are: tcp, unix, http" + "redis_host", "localhost", "IP or hostname of the target server (ignored for unix scheme)" + "redis_port", "6379", "TCP/IP port of the target server (ignored for unix scheme)" + "redis_path", "null", "Path of the UNIX domain socket file used when connecting to Redis using UNIX domain sockets" diff --git a/docs/en/user/share.rst b/docs/en/user/share.rst new file mode 100644 index 000000000..e99e51ab3 --- /dev/null +++ b/docs/en/user/share.rst @@ -0,0 +1,17 @@ +Share articles +============== + +When you're reading an article, you can share it. Just click on the share button: + +.. image:: ../../img/user/share.png + :alt: share article + :align: center + +Now, you can share the article: + +- with a public URL (you'll have a light view of the article) +- with a tweet +- into your Shaarli +- with a post in Diaspora* +- to Carrot +- with an email diff --git a/docs/en/user/upgrade-2.0.x-2.1.1.rst b/docs/en/user/upgrade-2.0.x-2.1.1.rst new file mode 100644 index 000000000..4f9b9ceeb --- /dev/null +++ b/docs/en/user/upgrade-2.0.x-2.1.1.rst @@ -0,0 +1,84 @@ +Upgrade from 2.0.x to 2.1.1 +=========================== + +.. warning:: + + Before this migration, if you configured the Pocket import by adding your consumer key in Internal settings, please do a backup of it: you'll have to add it into the Config page after the upgrade. + +Upgrade on a dedicated web server +--------------------------------- + +:: + + rm -rf var/cache/* + git fetch origin + git fetch --tags + git checkout 2.1.1 --force + SYMFONY_ENV=prod composer install --no-dev -o --prefer-dist + php bin/console doctrine:migrations:migrate --env=prod + php bin/console cache:clear --env=prod + +Upgrade on a shared hosting +--------------------------- + +Backup your ``app/config/parameters.yml`` file. + +Download the last release of wallabag: + +.. code-block:: bash + + wget http://framabag.org/wallabag-release-2.1.1.tar.gz && tar xvf wallabag-release-2.1.1.tar.gz + +(md5 hash of the 2.1.1 package: ``9584a3b60a2b2a4de87f536548caac93``) + +Extract the archive in your wallabag folder and replace ``app/config/parameters.yml`` with yours. + +Please check that your ``app/config/parameters.yml`` contains all the required parameters. Here is a default ``parameters.yml`` file. If you don't know which parameter you need to set, please leave the default one. + +.. code-block:: yml + + parameters: + database_driver: pdo_sqlite + database_host: 127.0.0.1 + database_port: null + database_name: symfony + database_user: root + database_password: null + database_path: '%kernel.root_dir%/../data/db/wallabag.sqlite' + database_table_prefix: wallabag_ + mailer_transport: smtp + mailer_host: 127.0.0.1 + mailer_user: null + mailer_password: null + locale: en + secret: ovmpmAWXRCabNlMgzlzFXDYmCFfzGv + twofactor_auth: true + twofactor_sender: no-reply@wallabag.org + fosuser_registration: true + fosuser_confirmation: true + from_email: no-reply@wallabag.org + rss_limit: 50 + rabbitmq_host: localhost + rabbitmq_port: 5672 + rabbitmq_user: guest + rabbitmq_password: guest + redis_host: localhost + redis_port: 6379 + +You can find `here a documentation about parameters `_. + +If you use SQLite, you must also copy your ``data/`` folder inside the new installation. + +Empty ``var/cache`` folder. + +You must run some SQL queries to upgrade your database. We assume that the table prefix is ``wallabag_`` and the database server is a MySQL one: + +.. code-block:: sql + + ALTER TABLE `wallabag_entry` ADD `uuid` LONGTEXT DEFAULT NULL; + INSERT INTO `wallabag_craue_config_setting` (`name`, `value`, `section`) VALUES ('share_public', '1', 'entry'); + ALTER TABLE `wallabag_oauth2_clients` ADD name longtext COLLATE 'utf8_unicode_ci' DEFAULT NULL; + INSERT INTO `wallabag_craue_config_setting` (`name`, `value`, `section`) VALUES ('import_with_redis', '0', 'import'); + INSERT INTO `wallabag_craue_config_setting` (`name`, `value`, `section`) VALUES ('import_with_rabbitmq', '0', 'import'); + ALTER TABLE `wallabag_config` ADD `pocket_consumer_key` VARCHAR(255) DEFAULT NULL; + DELETE FROM `wallabag_craue_config_setting` WHERE `name` = 'pocket_consumer_key'; diff --git a/docs/en/user/upgrade.rst b/docs/en/user/upgrade-2.1.x-2.1.y.rst similarity index 74% rename from docs/en/user/upgrade.rst rename to docs/en/user/upgrade-2.1.x-2.1.y.rst index 90ed6c701..62cb7dc0a 100644 --- a/docs/en/user/upgrade.rst +++ b/docs/en/user/upgrade-2.1.x-2.1.y.rst @@ -1,16 +1,17 @@ -Upgrade wallabag -================ +Upgrading from 2.1.x to 2.1.y +============================= Upgrade on a dedicated web server --------------------------------- -The last release is published on https://www.wallabag.org/pages/download-wallabag.html. In order to upgrade your wallabag installation and get the last version, run the following commands in you wallabag folder (replace ``2.0.8`` by the last release number): +The last release is published on https://www.wallabag.org/pages/download-wallabag.html. In order to upgrade your wallabag installation and get the last version, run the following commands in you wallabag folder (replace ``2.1.2`` by the last release number): :: + rm -rf var/cache/* git fetch origin git fetch --tags - git checkout 2.0.8 + git checkout 2.1.2 --force SYMFONY_ENV=prod composer install --no-dev -o --prefer-dist php bin/console cache:clear --env=prod @@ -25,7 +26,7 @@ Download the last release of wallabag: wget http://wllbg.org/latest-v2-package && tar xvf latest-v2-package -(md5 hash of the package: ``4f84c725d1d6e3345eae0a406115e5ff``) +You will find the `md5 hash of the latest package on our website `_. Extract the archive in your wallabag folder and replace ``app/config/parameters.yml`` with yours. diff --git a/docs/fr/conf.py b/docs/fr/conf.py index f1fe39676..49a57e2d5 100644 --- a/docs/fr/conf.py +++ b/docs/fr/conf.py @@ -12,7 +12,7 @@ source_suffix = '.rst' master_doc = 'index' project = u'wallabag-fr' copyright = u'2013-2016, Nicolas Lœuillet - MIT Licence' -version = '2.0.0' +version = '2.1.0' release = version exclude_patterns = ['_build'] pygments_style = 'sphinx' diff --git a/docs/fr/developer/maintenance.rst b/docs/fr/developer/maintenance.rst index 4ea8d01fe..8007a85fc 100644 --- a/docs/fr/developer/maintenance.rst +++ b/docs/fr/developer/maintenance.rst @@ -11,7 +11,7 @@ Pour activer le mode maintenance, exécutez cette commande : :: - bin/console lexik:maintenance:lock --no-interaction + bin/console lexik:maintenance:lock --no-interaction -e=prod Vous pouvez spécifier votre adresse IP dans ``app/config/config.yml`` si vous souhaitez accéder à wallabag même si le mode maintenance est activé. Par exemple : @@ -30,4 +30,4 @@ Pour désactiver le mode maintenance, exécutez cette commande : :: - bin/console lexik:maintenance:unlock + bin/console lexik:maintenance:unlock -e=prod diff --git a/docs/fr/developer/rabbitmq.rst b/docs/fr/developer/rabbitmq.rst new file mode 100644 index 000000000..92db5a28d --- /dev/null +++ b/docs/fr/developer/rabbitmq.rst @@ -0,0 +1,80 @@ +Installer RabbitMQ pour des tâches asynchrones +============================================== + +Pour lancer des tâches asynchrones (utile pour des imports importants par exemple), nous pouvons utiliser RabbitMQ. + +Pré-requis +---------- + +Vous devez installer RabbitMQ sur votre serveur. + +Installation +~~~~~~~~~~~~ + +.. code:: bash + + wget https://www.rabbitmq.com/rabbitmq-signing-key-public.asc + apt-key add rabbitmq-signing-key-public.asc + apt-get update + apt-get install rabbitmq-server + +Configuration et démarrage +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: bash + + rabbitmq-plugins enable rabbitmq_management # (useful to have a web interface, available at http://localhost:15672/ (guest/guest) + rabbitmq-server -detached + +Arrêter RabbitMQ +~~~~~~~~~~~~~~~~ + +.. code:: bash + + rabbitmqctl stop + + +Configurer RabbitMQ dans wallabag +--------------------------------- + +Modifiez votre fichier ``parameters.yml`` pour éditer la configuration RabbitMQ. Celle par défaut devrait convenir : + +.. code:: yaml + + rabbitmq_host: localhost + rabbitmq_port: 5672 + rabbitmq_user: guest + rabbitmq_password: guest + +Activer RabbitMQ dans wallabag +------------------------------ + +Dans les paramètres internes, section **Import**, activez RabbitMQ (avec la valeur 1). + +Démarrer les clients RabbitMQ +----------------------------- + +En fonction du service dont vous souhaitez importer vos données, vous devez activer un (ou plusieurs si vous souhaitez en supporter plusieurs) cron job : + +.. code:: bash + + # for Pocket import + bin/console rabbitmq:consumer -e=prod import_pocket -w + + # for Readability import + bin/console rabbitmq:consumer -e=prod import_readability -w + + # for Instapaper import + bin/console rabbitmq:consumer -e=prod import_instapaper -w + + # for wallabag v1 import + bin/console rabbitmq:consumer -e=prod import_wallabag_v1 -w + + # for wallabag v2 import + bin/console rabbitmq:consumer -e=prod import_wallabag_v2 -w + + # for Firefox import + bin/console rabbitmq:consumer -e=prod import_firefox -w + + # for Chrome import + bin/console rabbitmq:consumer -e=prod import_chrome -w diff --git a/docs/fr/developer/redis.rst b/docs/fr/developer/redis.rst new file mode 100644 index 000000000..8a212e8aa --- /dev/null +++ b/docs/fr/developer/redis.rst @@ -0,0 +1,75 @@ +Installer Redis pour des tâches asynchrones +=========================================== + +Pour lancer des tâches asynchrones (utile pour des imports importants par exemple), nous pouvons utiliser Redis. + +Pré-requis +---------- + +Vous devez installer Redis sur votre serveur. + +Installation +~~~~~~~~~~~~ + +.. code:: bash + + apt-get install redis-server + +Démarrage +~~~~~~~~~ + +Le serveur devrait déjà être démarré après l'installation. Si ce n'est pas le cas, vous pouvez le démarrer ainsi : + +.. code:: bash + + redis-server + + +Configurer Redis dans wallabag +------------------------------- + +Modifiez votre fichier ``parameters.yml`` pour éditer la configuration Redis. Celle par défaut devrait convenir : + +.. code:: yaml + + redis_host: localhost + redis_port: 6379 + +Activer Redis dans wallabag +--------------------------- + +Dans les paramètres internes, section **Import**, activez Redis (avec la valeur 1). + +Démarrer les clients Redis +-------------------------- + +En fonction du service dont vous souhaitez importer vos données, vous devez activer un (ou plusieurs si vous souhaitez en supporter plusieurs) cron job : + +.. code:: bash + + # for Pocket import + bin/console wallabag:import:redis-worker -e=prod pocket -vv >> /path/to/wallabag/var/logs/redis-pocket.log + + # for Readability import + bin/console wallabag:import:redis-worker -e=prod readability -vv >> /path/to/wallabag/var/logs/redis-readability.log + + # for Instapaper import + bin/console wallabag:import:redis-worker -e=prod instapaper -vv >> /path/to/wallabag/var/logs/redis-instapaper.log + + # for wallabag v1 import + bin/console wallabag:import:redis-worker -e=prod wallabag_v1 -vv >> /path/to/wallabag/var/logs/redis-wallabag_v1.log + + # for wallabag v2 import + bin/console wallabag:import:redis-worker -e=prod wallabag_v2 -vv >> /path/to/wallabag/var/logs/redis-wallabag_v2.log + + # for Firefox import + bin/console wallabag:import:redis-worker -e=prod firefox -vv >> /path/to/wallabag/var/logs/redis-firefox.log + + # for Chrome import + bin/console wallabag:import:redis-worker -e=prod instapaper -vv >> /path/to/wallabag/var/logs/redis-chrome.log + +Si vous souhaitez démarrer l'import pour quelques messages uniquement, vous pouvez spécifier cette valeur en paramètre (ici 12) et le client va s'arrêter après le 12ème message : + +.. code:: bash + + bin/console wallabag:import:redis-worker -e=prod pocket -vv --maxIterations=12 diff --git a/docs/fr/developer/testsuite.rst b/docs/fr/developer/testsuite.rst new file mode 100644 index 000000000..bd5c68c6b --- /dev/null +++ b/docs/fr/developer/testsuite.rst @@ -0,0 +1,10 @@ +Suite de tests +============== + +Pour assurer la qualité du développement de wallabag, nous avons écrit des tests avec `PHPUnit `_. + +Si vous contribuez au projet (en traduisant l'application, en corrigeant des bugs ou en ajoutant une nouvelle fonctionnalité), merci d'écrire vos propres tests. + +Pour lancer la suite de tests de wallabag, vous devez installer `ant `_. + +Ensuite, exécutez la commande ``make test``. diff --git a/docs/fr/index.rst b/docs/fr/index.rst index 1e72dae78..2a265ff34 100644 --- a/docs/fr/index.rst +++ b/docs/fr/index.rst @@ -18,6 +18,11 @@ La documentation principale de cette application est découpée en plusieurs sec * :ref:`user-docs` * :ref:`dev-docs` +La documentation est disponible dans d'autres langues : + +* `Documentation in english `_ +* `Deutsch Dokumentation `_ + .. _user-docs: .. toctree:: @@ -26,7 +31,8 @@ La documentation principale de cette application est découpée en plusieurs sec user/faq user/installation - user/upgrade + user/upgrade-2.0.x-2.1.1 + user/upgrade-2.0.x-2.1.1 user/migration user/import user/create_account @@ -36,8 +42,10 @@ La documentation principale de cette application est découpée en plusieurs sec user/errors_during_fetching user/annotations user/download_articles + user/share user/filters user/tags + user/parameters .. _dev-docs: @@ -50,3 +58,5 @@ La documentation principale de cette application est découpée en plusieurs sec developer/documentation developer/translate developer/maintenance + developer/redis + developer/rabbitmq diff --git a/docs/fr/user/configuration.rst b/docs/fr/user/configuration.rst index 278f0022f..8bfe66f54 100644 --- a/docs/fr/user/configuration.rst +++ b/docs/fr/user/configuration.rst @@ -102,7 +102,7 @@ if *« readingTime >= 5 AND domainName = "github.com" »* then tag as *« long r Quels variables et opérateurs puis-je utiliser pour écrire mes règles ? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Les variables et opérateurs suivants peuvent être utilisés lors de la création de vos règles : +Les variables et opérateurs suivants peuvent être utilisés lors de la création de vos règles (attention, pour certaines valeurs, vous devez ajouter des guillemets, par exemple ``language = "en"``) : =========== ============================================== ========== ========== Variable Sens Opérateur Sens diff --git a/docs/fr/user/errors_during_fetching.rst b/docs/fr/user/errors_during_fetching.rst index 74290bef4..1a60e4bdb 100644 --- a/docs/fr/user/errors_during_fetching.rst +++ b/docs/fr/user/errors_during_fetching.rst @@ -12,9 +12,21 @@ Il peut y avoir plusieurs raisons : Comment puis-je aider pour réparer ça ? --------------------------------------- -- `en nous envoyant un email avec l'URL de l'article `_ -- en essayant de réparer cet article par vous-même :) en créant un fichier pour l'article. - Vous pouvez utiliser `cet outil `__. +Vous pouvez `nous envoyer un email avec l'URL de l'article `_. + +Ou vous pouvez aussi essayer de résoudre ce problème vous même (comme ça, nous restons concentrés pour améliorer wallabag au lieu d'écrire ces fichiers de configuration :) ). + +Vous pouvez essayer de voir si ça fonctionne ici : `http://f43.me/feed/test `_ (ce site utilise principalement la même manière de fonctionner que wallabag pour récupérer les articles). + +Si ça fonctionne ici et pas sur wallabag, c'est qu'il y a un souci avec wallabag qui casse le parser (difficile à résoudre : merci d'ouvrir un nouveau ticket à ce sujet). + +Si ça ne fonctionne pas, vous pouvez essayer de créer un fichier de configuration en utilisant : `http://siteconfig.fivefilters.org/ `_ (sélectionnez les parties du contenu qui correspondent à ce que vous souhaitez garder). Vous pouvez `lire cette documentation avant `_. + +Vous pouvez tester ce fichier sur le site **f43.me** : cliquez sur **Want to try a custom siteconfig?** et insérez le fichier généré depuis siteconfig.fivefilters.org. + +Répétez cette opération jusqu'à avoir quelque chose qui vous convienne. + +Ensuite, vous pouvez créer une pull request ici `https://github.com/fivefilters/ftr-site-config `_, qui est le projet principal pour stocker les fichiers de configuration. Comment puis-je réessayer de récupérer le contenu ? --------------------------------------------------- diff --git a/docs/fr/user/import.rst b/docs/fr/user/import.rst index e6c2fa02d..2e6caafba 100644 --- a/docs/fr/user/import.rst +++ b/docs/fr/user/import.rst @@ -28,17 +28,40 @@ la section Pocket puis sur ``Se connecter à Pocket et importer les données``. Vous devez autoriser wallabag à se connecter à votre compte Pocket. Vos données vont être importées. L'import de données est une action qui peut être couteuse -pour votre serveur (nous devons encore travailler pour améliorer cet import). - -Depuis Instapaper ------------------ - -*Fonctionnalité pas encore implémentée dans wallabag v2.* +pour votre serveur. Depuis Readability ------------------ -*Fonctionnalité pas encore implémentée dans wallabag v2.* +Exportez vos données de Readability +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sur la page des outils (`https://www.readability.com/tools/ `_), cliquez sur "Export your data" dans la section "Data Export". Vous allez recevoir un email avec un lien pour télécharger le json. + +Importez vos données dans wallabag 2.x +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Cliquez sur le lien ``Importer`` dans le menu, sur ``Importer les contenus`` dans +la section Readability et ensuite sélectionnez votre fichier json pour l'uploader. + +Vos données vont être importées. L'import de données est une action qui peut être couteuse pour votre serveur. + +Depuis Instapaper +----------------- + +Exportez vos données de Instapaper +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sur la page des paramètres (`https://www.instapaper.com/user `_), cliquez sur "Download .CSV file" dans la section "Export". Un fichier CSV se téléchargera (``instapaper-export.csv``). + +Importez vos données dans wallabag 2.x +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Cliquez sur le lien ``Importer`` dans le menu, sur ``Importer les contenus`` dans +la section Instapaper et ensuite sélectionnez votre fichier CSV pour l'uploader. + +Vos données vont être importées. L'import de données est une action qui peut être couteuse pour votre serveur. + Depuis un fichier HTML ou JSON ------------------------------ diff --git a/docs/fr/user/installation.rst b/docs/fr/user/installation.rst index 480970c53..4f94d6c87 100644 --- a/docs/fr/user/installation.rst +++ b/docs/fr/user/installation.rst @@ -6,6 +6,20 @@ Pré-requis wallabag est compatible avec PHP >= 5.5, PHP 7 inclus. +.. note:: + + Pour installer wallabag facilement, nous avons créé un ``Makefile``, vous avez donc besoin d'avoir installé l'outil ``make``. + +wallabag utilise un grand nombre de bibliothèques PHP pour fonctionner. Ces bibliothèques doivent être installées à l'aide d'un outil nommé Composer. Vous devez l'installer si ce n'est déjà fait et vous assurer que vous utilisez bien la version 1.2 (si vous avez déjà Composer, faite un ``composer selfupdate``). + +Installation de Composer : + +:: + + curl -s http://getcomposer.org/installer | php + +Vous pouvez trouver des instructions spécifiques `ici (en anglais) `__. + Vous aurez besoin des extensions suivantes pour que wallabag fonctionne. Il est possible que certaines de ces extensions soient déjà activées dans votre version de PHP, donc vous n'avez pas forcément besoin d'installer tous les paquets correspondants. - php-session @@ -22,6 +36,7 @@ Vous aurez besoin des extensions suivantes pour que wallabag fonctionne. Il est - php-curl - php-gettext - php-tokenizer +- php-bcmath wallabag utilise PDO afin de se connecter à une base de données, donc vous aurez besoin d'une extension et d'un système de bases de données parmi : @@ -35,36 +50,23 @@ Installation Sur un serveur dédié (méthode conseillée) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -wallabag utilise un grand nombre de bibliothèques pour fonctionner. Ces bibliothèques doivent être installées à l'aide d'un outil nommé Composer. Vous devez l'installer si ce n'est déjà fait et vous assurer que vous utilisez bien la version 1.2 (si vous avez déjà Composer, faite un ``composer selfupdate``). - -Installation de Composer : - -:: - - curl -s http://getcomposer.org/installer | php - -Vous pouvez trouver des instructions spécifiques `ici (en anglais) `__ : - -Pour installer wallabag, vous devez exécuter ces deux commandes : +Pour installer wallabag, vous devez exécuter ces commandes : :: git clone https://github.com/wallabag/wallabag.git - cd wallabag - git checkout 2.0.8 - SYMFONY_ENV=prod composer install --no-dev -o --prefer-dist - php bin/console wallabag:install --env=prod + cd wallabag && make install Pour démarrer le serveur interne à php et vérifier que tout s'est installé correctement, vous pouvez exécuter : :: - php bin/console server:run --env=prod + make run Et accéder wallabag à l'adresse http://lipdevotreserveur:8000 .. tip:: - Pour définir des paramètres via des variables d'environnement, vous pouvez les spécifier avec le préfixe ``SYMFONY__``. Par exemple, ``SYMFONY__DATABASE_DRIVER``. Vous pouvez lire `documentation Symfony `__ pour en savoir plus. + Pour définir des paramètres via des variables d'environnement, vous pouvez les spécifier avec le préfixe ``SYMFONY__``. Par exemple, ``SYMFONY__DATABASE_DRIVER``. Vous pouvez lire la `documentation Symfony `__ pour en savoir plus. Sur un serveur mutualisé ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -82,18 +84,18 @@ Exécutez cette commande pour télécharger et décompresser l'archive : wget http://wllbg.org/latest-v2-package && tar xvf latest-v2-package -(hash md5 de l'archive : ``4f84c725d1d6e3345eae0a406115e5ff``) +Vous trouverez `le hash md5 du dernier package sur notre site `_. Maintenant, lisez la documentation ci-dessous pour crééer un virtual host. Accédez ensuite à votre installation de wallabag. Si vous avez changé la configuration pour modifier le type de stockage (MySQL ou PostgreSQL), vous devrez vous créer un utilisateur via la commande ``php bin/console wallabag:install --env=prod``. Installation avec Docker ------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~ Nous vous proposons une image Docker pour installer wallabag facilement. Allez voir du côté de `Docker Hub `__ pour plus d'informations. Commande pour démarrer le containeur -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: bash diff --git a/docs/fr/user/migration.rst b/docs/fr/user/migration.rst index 66024572b..91f8bab2e 100644 --- a/docs/fr/user/migration.rst +++ b/docs/fr/user/migration.rst @@ -24,29 +24,6 @@ Une fois que vous avez créé un compte utilisateur sur votre nouvelle instance :alt: Import depuis wallabag v1 :align: center -Import via via la ligne de commande (CLI) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Si vous avez accès à la ligne de commandes de votre serveur web, vous pouvez exécuter cette commande pour import votre fichier wallabag v1 : - -:: - - bin/console wallabag:import-v1 1 ~/Downloads/wallabag-export-1-2016-04-05.json --env=prod - -Remplacez les valeurs : - -* ``1`` est l'identifiant de votre utilisateur en base (l'ID de votre premier utilisateur créé sur wallabag est 1) -* ``~/Downloads/wallabag-export-1-2016-04-05.json`` est le chemin de votre export wallabag v1 - -Vous obtiendrez : - -:: - - Start : 05-04-2016 11:36:07 --- - 403 imported - 0 already saved - End : 05-04-2016 11:36:09 --- - Depuis wallabag 2.x ------------------- @@ -60,3 +37,30 @@ Depuis votre nouvelle instance de wallabag, créez votre compte utilisateur puis .. note:: S'il vous arrive des problèmes durant l'export ou l'import, n'hésitez pas à `demander de l'aide `__. + +Import via la ligne de commande (CLI) +------------------------------------- + +Si vous avez accès à la ligne de commandes de votre serveur web, vous pouvez exécuter cette commande pour import votre fichier wallabag v1 : + +:: + + bin/console wallabag:import 1 ~/Downloads/wallabag-export-1-2016-04-05.json --env=prod + +Remplacez les valeurs : + +* ``1`` est l'identifiant de votre utilisateur en base (l'ID de votre premier utilisateur créé sur wallabag est 1) +* ``~/Downloads/wallabag-export-1-2016-04-05.json`` est le chemin de votre export wallabag v1 + +Si vous voulez marquer tous ces articles comme lus, vous pouvez ajouter l'option ``--markAsRead``. + +Pour importer un fichier wallabag v2, vous devez ajouter l'option ``--importer=v2``. + +Vous obtiendrez : + +:: + + Start : 05-04-2016 11:36:07 --- + 403 imported + 0 already saved + End : 05-04-2016 11:36:09 --- diff --git a/docs/fr/user/parameters.rst b/docs/fr/user/parameters.rst new file mode 100644 index 000000000..ec8c8995d --- /dev/null +++ b/docs/fr/user/parameters.rst @@ -0,0 +1,50 @@ +À quoi servent les paramètres ? +=============================== +.. csv-table:: Paramètres de base de données + :header: "name", "default", "description" + + "database_driver", "pdo_sqlite", "Doit être pdo_sqlite ou pdo_mysql ou pdo_pgsql" + "database_host", "127.0.0.1", "Hôte de votre base de données (généralement localhost ou 127.0.0.1)" + "database_port", "~", "Port de votre base de données (vous pouvez laisser ``~`` pour utiliser celui par défaut)" + "database_name", "symfony", "Nom de votre base de données" + "database_user", "root", "Utilisateur de votre base de données" + "database_password", "~", "Mot de passe de cet utilisateur" + "database_path", "``""%kernel.root_dir%/../data/db/wallabag.sqlite""``", "Uniquement pour SQLite. Chemin du fichier de base de données. Laissez vide pour les autres bases de données." + "database_table_prefix", "wallabag_", "Toutes les tables de wallabag seront préfixées par cette chaine. Vous pouvez ajouter un ``_`` pour plus de clarté" + "database_socket", "null", "Si votre base de données utilise un socket plutôt que tcp, spécifiez le chemin du socket (les autres paramètres de connexion seront alors ignorés)" + +.. csv-table:: Configuration pour envoyer des emails depuis wallabag + :header: "name", "default", "description" + + "mailer_transport", "smtp", "Méthode de transport exacte utilisée pour envoyer des emails. Les valeurs correctes sont : smtp, gmail, mail, sendmail, null (ce qui désactivera l'envoi des emails)" + "mailer_host", "127.0.0.1", "Hôte sur lequel se connecter quand on utilise smtp comme transport." + "mailer_user", "~", "Utilisateur smtp." + "mailer_password", "~", "Mot de passe de cet utilisateur." + +.. csv-table:: Autres options de wallabag + :header: "name", "default", "description" + + "locale", "en", "Langue par défaut de votre instance wallabag (comme en, fr, es, etc.)" + "secret", "ovmpmAWXRCabNlMgzlzFXDYmCFfzGv", "C'est une chaine qui doit être unique à votre application et qui est couramment utilisée pour ajouter plus d'entropie aux opérations relatives à la sécurité." + "twofactor_auth", "true", "true pour activer l'authentification à deux facteurs" + "twofactor_sender", "no-reply@wallabag.org", "Email de l'expéditeur du code de l'authentification à deux facteurs" + "fosuser_registration", "true", "true pour activer l'inscription publique" + "fosuser_confirmation", "true", "true pour envoyer un email de confirmation pour chaque création de compte" + "from_email", "no-reply@wallabag.org", "Email de l'expéditeur pour chaque email envoyé" + "rss_limit", "50", "Limite pour les flux RSS" + +.. csv-table:: Configuration RabbitMQ + :header: "name", "default", "description" + + "rabbitmq_host", "localhost", "Hôte de votre instance RabbitMQ" + "rabbitmq_port", "5672", "Port de votre instance RabbitMQ" + "rabbitmq_user", "guest", "Utilisateur de votre instance RabbitMQ" + "rabbitmq_password", "guest", "Mot de passe de cet utilisateur" + +.. csv-table:: Configuration Redis + :header: "name", "default", "description" + + "redis_scheme", "tcp", "Définit le protocole utilisé pour commuiquer avec l'instance Redis. Les valeurs correctes sont : tcp, unix, http" + "redis_host", "localhost", "IP ou hôte du serveur cible (ignoré pour un schéma unix)" + "redis_port", "6379", "Port TCP/IP du serveur cible (ignoré pour un schéma unix)" + "redis_path", "null", "Chemin du fichier de socket du domaine UNIX utilisé quand on se connecte à Redis en utilisant les sockets du domaine UNIX" diff --git a/docs/fr/user/share.rst b/docs/fr/user/share.rst new file mode 100644 index 000000000..b5faa5ba6 --- /dev/null +++ b/docs/fr/user/share.rst @@ -0,0 +1,17 @@ +Partager des articles +===================== + +Quand vous lisez un article, vous pouvez le partager. Cliquez sur le bouton de partage : + +.. image:: ../../img/user/share.png + :alt: partager un article + :align: center + +Vous pouvez maintenant le partager : + +- avec une URL publique (vous obtiendrez une vue allégée de l'article) +- avec un tweet +- dans votre Shaarli +- avec un message dans Diaspora* +- sur Carrot +- avec un email diff --git a/docs/fr/user/upgrade-2.0.x-2.1.1.rst b/docs/fr/user/upgrade-2.0.x-2.1.1.rst new file mode 100644 index 000000000..ec883d10e --- /dev/null +++ b/docs/fr/user/upgrade-2.0.x-2.1.1.rst @@ -0,0 +1,84 @@ +Mettre à jour de la 2.0.x à la 2.1.1 +==================================== + +.. warning:: +Avant cette migration, si vous aviez configuré l'import depuis Pocket en ajoutant votre consumer key dans les paramètres internes, pensez à effectuer une sauvegarde de celle-ci : vous devrez l'ajouter dans la configuration de wallabag après la mise à jour. + +Mise à jour sur un serveur dédié +-------------------------------- + +:: + + rm -rf var/cache/* + git fetch origin + git fetch --tags + git checkout 2.1.1 --force + SYMFONY_ENV=prod composer install --no-dev -o --prefer-dist + php bin/console doctrine:migrations:migrate --env=prod + php bin/console cache:clear --env=prod + +Mise à jour sur un hébergement mutualisé +---------------------------------------- + +Effectuez une sauvegarde du fichier ``app/config/parameters.yml``. + +Téléchargez la dernière version de wallabag : + +.. code-block:: bash + + wget http://framabag.org/wallabag-release-2.1.1.tar.gz && tar xvf wallabag-release-2.1.1.tar.gz + +(hash md5 de l'archive 2.1.1 : ``9584a3b60a2b2a4de87f536548caac93``) + +Décompressez l'archive dans votre répertoire d'installation et remplacez le fichier ``app/config/parameters.yml`` avec le votre. + +Vérifiez que votre fichier ``app/config/parameters.yml`` contient tous les paramètres requis. Voici un fichier ``parameters.yml`` par défaut. Si vous ne savez pas quelle valeur mettre à un paramètre, laissez la valeur par défaut. + +.. code-block:: yml + + parameters: + database_driver: pdo_sqlite + database_host: 127.0.0.1 + database_port: null + database_name: symfony + database_user: root + database_password: null + database_path: '%kernel.root_dir%/../data/db/wallabag.sqlite' + database_table_prefix: wallabag_ + mailer_transport: smtp + mailer_host: 127.0.0.1 + mailer_user: null + mailer_password: null + locale: en + secret: ovmpmAWXRCabNlMgzlzFXDYmCFfzGv + twofactor_auth: true + twofactor_sender: no-reply@wallabag.org + fosuser_registration: true + fosuser_confirmation: true + from_email: no-reply@wallabag.org + rss_limit: 50 + rabbitmq_host: localhost + rabbitmq_port: 5672 + rabbitmq_user: guest + rabbitmq_password: guest + redis_host: localhost + redis_port: 6379 + +Vous trouverez `ici une documentation détaillée concernant les paramètres `_. + +Si vous utilisez SQLite, vous devez également conserver le contenu du répertoire ``data/``. + +Videz le répertoire ``var/cache``. + +Vous allez devoir également exécuter des requêtes SQL pour mettre à jour votre base de données. Nous partons du principe que le préfixe de vos tables est ``wallabag_`` et que le serveur SQL est un serveur MySQL : + +.. code-block:: sql + + ALTER TABLE `wallabag_entry` ADD `uuid` LONGTEXT DEFAULT NULL; + INSERT INTO `wallabag_craue_config_setting` (`name`, `value`, `section`) VALUES ('share_public', '1', 'entry'); + ALTER TABLE `wallabag_oauth2_clients` ADD name longtext COLLATE 'utf8_unicode_ci' DEFAULT NULL; + INSERT INTO `wallabag_craue_config_setting` (`name`, `value`, `section`) VALUES ('import_with_redis', '0', 'import'); + INSERT INTO `wallabag_craue_config_setting` (`name`, `value`, `section`) VALUES ('import_with_rabbitmq', '0', 'import'); + ALTER TABLE `wallabag_config` ADD `pocket_consumer_key` VARCHAR(255) DEFAULT NULL; + DELETE FROM `wallabag_craue_config_setting` WHERE `name` = 'pocket_consumer_key'; + diff --git a/docs/fr/user/upgrade.rst b/docs/fr/user/upgrade-2.1.x-2.1.y.rst similarity index 77% rename from docs/fr/user/upgrade.rst rename to docs/fr/user/upgrade-2.1.x-2.1.y.rst index 1ead2c946..b6f1a2d09 100644 --- a/docs/fr/user/upgrade.rst +++ b/docs/fr/user/upgrade-2.1.x-2.1.y.rst @@ -1,16 +1,17 @@ -Mettre à jour wallabag -====================== +Mettre à jour de la 2.1.x à la 2.1.y +==================================== Mise à jour sur un serveur dédié -------------------------------- -La dernière version de wallabag est publiée à cette adresse : https://www.wallabag.org/pages/download-wallabag.html. Pour mettre à jour votre installation de wallabag, exécutez les commandes suivantes dans votre répertoire d'installation (remplacez ``2.0.8`` par le numéro de la dernière version) : +La dernière version de wallabag est publiée à cette adresse : https://www.wallabag.org/pages/download-wallabag.html. Pour mettre à jour votre installation de wallabag, exécutez les commandes suivantes dans votre répertoire d'installation (remplacez ``2.1.2`` par le numéro de la dernière version) : :: + rm -rf var/cache/* git fetch origin git fetch --tags - git checkout 2.0.8 + git checkout 2.1.2 --force SYMFONY_ENV=prod composer install --no-dev -o --prefer-dist php bin/console cache:clear --env=prod @@ -25,7 +26,7 @@ Téléchargez la dernière version de wallabag : wget http://wllbg.org/latest-v2-package && tar xvf latest-v2-package -(hash md5 de l'archive : ``4f84c725d1d6e3345eae0a406115e5ff``) +Vous trouverez `le hash md5 du dernier package sur notre site `_. Décompressez l'archive dans votre répertoire d'installation et remplacez le fichier ``app/config/parameters.yml`` avec le votre. diff --git a/docs/img/user/share.png b/docs/img/user/share.png new file mode 100644 index 000000000..4cfe9edb9 Binary files /dev/null and b/docs/img/user/share.png differ diff --git a/package.json b/package.json new file mode 100644 index 000000000..20afb4259 --- /dev/null +++ b/package.json @@ -0,0 +1,103 @@ +{ + "name": "wallabag", + "version": "2.1.0", + "description": "wallabag is a self hostable application for saving web pages", + "main": "index.js", + "private": true, + "directories": { + "doc": "docs" + }, + "engines": { + "node": ">0.12" + }, + "browser": { + "jquery": "./node_modules/jquery/dist/jquery.js", + "jQuery": "./node_modules/jquery/dist/jquery.js", + "materialize": "./node_modules/materialize-css/bin/materialize.js" + }, + "browserify-shim": { + "jquery": { + "exports": "$" + }, + "materialize": "materialize", + "jquery-ui": { + "depends": "jquery", + "exports": null + } + }, + "browserify": { + "transform": [ + "browserify-shim" + ] + }, + "repository": { + "type": "git", + "url": "https://github.com/wallabag/wallabag" + }, + "contributors": [ + { + "name": "Nicolas Lœuillet", + "email": "nicolas@loeuillet.org", + "homepage": "http://www.cdetc.fr", + "role": "Developer" + }, + { + "name": "Thomas Citharel", + "homepage": "http://tcit.fr", + "role": "Developer" + }, + { + "name": "Jérémy Benoist", + "homepage": "http://www.j0k3r.net", + "role": "Developer" + } + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/wallabag/wallabag/issues" + }, + "devDependencies": { + "annotator": "git://github.com/wallabag/annotator.git#0f076c7d371ed25eb0793346f46982d90f2c4c85", + "autoprefixer": "^6.3.6", + "babel-eslint": "^6.1.2", + "babel-preset-es2015": "^6.14.0", + "babelify": "^7.3.0", + "browserify": "^13.0.0", + "browserify-shim": "^3.8.12", + "cssnano": "^3.5.2", + "es6-promise": "^3.2.1", + "eslint": "^3.7.1", + "eslint-config-airbnb-base": "^8.0.0", + "eslint-plugin-import": "^1.16.0", + "grunt": ">=0.4.0", + "grunt-browserify": "^5.0.0", + "grunt-cli": "^1.2.0", + "grunt-contrib-clean": "^1.0.0", + "grunt-contrib-concat": "^1.0.0", + "grunt-contrib-copy": "^1.0.0", + "grunt-contrib-symlink": "^1.0.0", + "grunt-contrib-uglify": "^1.0.0", + "grunt-eslint": "^19.0.0", + "grunt-postcss": "^0.8.0", + "grunt-stylelint": "^0.6.0", + "hammerjs": "^2.0.6", + "icomoon-free-npm": "0.0.0", + "jquery": "^2.2.4", + "jquery-ui-browserify": "^1.11.0-pre-seelio", + "jquery.cookie": "^1.4.1", + "jquery.tinydot": "^0.2.1", + "load-grunt-tasks": "^3.4.1", + "material-design-icons-iconfont": "^3.0.0", + "materialize-css": "0.97.5", + "npm": "^3.8.3", + "pickadate": "^3.5.6", + "pixrem": "^3.0.0", + "postcss-cssnext": "^2.5.1", + "prismjs": "^1.4.1", + "ptsans-npm-webfont": "0.0.4", + "roboto-fontface": "^0.6.0", + "stylelint": "^7.3.1", + "stylelint-config-standard": "^13.0.2", + "through": "^2.3.8" + } +} diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 388809080..3216b6ffb 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -16,6 +16,7 @@ + diff --git a/scripts/dev.sh b/scripts/dev.sh new file mode 100644 index 000000000..9b89da35f --- /dev/null +++ b/scripts/dev.sh @@ -0,0 +1,7 @@ +#! /usr/bin/env bash +# You can execute this file to install wallabag dev environmnet +# eg: `sh install.sh prod` + +composer install +php bin/console wallabag:install +php bin/console server:run diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100644 index 000000000..54d0bb785 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,10 @@ +#! /usr/bin/env bash +# You can execute this file to install wallabag +# eg: `sh install.sh prod` + +ENV=$1 +TAG=$(git describe --tags $(git rev-list --tags --max-count=1)) + +git checkout $TAG +SYMFONY_ENV=$ENV composer install --no-dev -o --prefer-dist +php bin/console wallabag:install --env=$ENV diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100644 index 000000000..e118b87ed --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,19 @@ +#! /usr/bin/env bash +# You can execute this file to create a new package for wallabag +# eg: `sh release.sh master /tmp wllbg-release prod` + +VERSION=$1 +TMP_FOLDER=$2 +RELEASE_FOLDER=$3 +ENV=$4 + +rm -rf $TMP_FOLDER/$RELEASE_FOLDER +mkdir $TMP_FOLDER/$RELEASE_FOLDER +git clone git@github.com:wallabag/wallabag.git -b $VERSION $TMP_FOLDER/$RELEASE_FOLDER/$VERSION +cd $TMP_FOLDER/$RELEASE_FOLDER/$VERSION && SYMFONY_ENV=$ENV composer up -n --no-dev +cd $TMP_FOLDER/$RELEASE_FOLDER/$VERSION && php bin/console wallabag:install --env=$ENV +cd $TMP_FOLDER/$RELEASE_FOLDER && tar czf wallabag-$VERSION.tar.gz --exclude="var/cache/*" --exclude="var/logs/*" --exclude="var/sessions/*" --exclude=".git" $VERSION +echo "MD5 checksum of the package for wallabag $VERSION" +md5 $TMP_FOLDER/$RELEASE_FOLDER/wallabag-$VERSION.tar.gz +scp $TMP_FOLDER/$RELEASE_FOLDER/wallabag-$VERSION.tar.gz framasoft_bag@78.46.248.87:/var/www/framabag.org/web +rm -rf $TMP_FOLDER/$RELEASE_FOLDER diff --git a/scripts/update.sh b/scripts/update.sh new file mode 100644 index 000000000..b920a829b --- /dev/null +++ b/scripts/update.sh @@ -0,0 +1,13 @@ +#! /usr/bin/env bash +# You can execute this file to update wallabag +# eg: `sh update.sh prod` + +ENV=$1 +TAG=$(git describe --tags $(git rev-list --tags --max-count=1)) + +rm -rf var/cache/* +git fetch origin +git fetch --tags +git checkout $TAG --force +SYMFONY_ENV=$ENV composer install --no-dev -o --prefer-dist +php bin/console cache:clear --env=$ENV diff --git a/src/Wallabag/AnnotationBundle/Entity/Annotation.php b/src/Wallabag/AnnotationBundle/Entity/Annotation.php index 90ee7c2d7..c48d87310 100644 --- a/src/Wallabag/AnnotationBundle/Entity/Annotation.php +++ b/src/Wallabag/AnnotationBundle/Entity/Annotation.php @@ -7,6 +7,7 @@ use JMS\Serializer\Annotation\ExclusionPolicy; use JMS\Serializer\Annotation\Exclude; use JMS\Serializer\Annotation\VirtualProperty; use JMS\Serializer\Annotation\SerializedName; +use JMS\Serializer\Annotation\Groups; use Wallabag\UserBundle\Entity\User; use Wallabag\CoreBundle\Entity\Entry; @@ -33,6 +34,8 @@ class Annotation * @var string * * @ORM\Column(name="text", type="text") + * + * @Groups({"entries_for_user", "export_all"}) */ private $text; @@ -54,6 +57,8 @@ class Annotation * @var string * * @ORM\Column(name="quote", type="string") + * + * @Groups({"entries_for_user", "export_all"}) */ private $quote; @@ -61,6 +66,8 @@ class Annotation * @var array * * @ORM\Column(name="ranges", type="array") + * + * @Groups({"entries_for_user", "export_all"}) */ private $ranges; diff --git a/src/Wallabag/CoreBundle/Controller/DeveloperController.php b/src/Wallabag/ApiBundle/Controller/DeveloperController.php similarity index 76% rename from src/Wallabag/CoreBundle/Controller/DeveloperController.php rename to src/Wallabag/ApiBundle/Controller/DeveloperController.php index d85ba5ee4..5a36a2605 100644 --- a/src/Wallabag/CoreBundle/Controller/DeveloperController.php +++ b/src/Wallabag/ApiBundle/Controller/DeveloperController.php @@ -1,12 +1,12 @@ getDoctrine()->getRepository('WallabagApiBundle:Client')->findAll(); - return $this->render('WallabagCoreBundle:Developer:index.html.twig', [ + return $this->render('@WallabagCore/themes/common/Developer/index.html.twig', [ 'clients' => $clients, ]); } @@ -49,16 +49,17 @@ class DeveloperController extends Controller $this->get('session')->getFlashBag()->add( 'notice', - 'flashes.developer.notice.client_created' + $this->get('translator')->trans('flashes.developer.notice.client_created', ['%name%' => $client->getName()]) ); - return $this->render('WallabagCoreBundle:Developer:client_parameters.html.twig', [ + return $this->render('@WallabagCore/themes/common/Developer/client_parameters.html.twig', [ 'client_id' => $client->getPublicId(), 'client_secret' => $client->getSecret(), + 'client_name' => $client->getName(), ]); } - return $this->render('WallabagCoreBundle:Developer:client.html.twig', [ + return $this->render('@WallabagCore/themes/common/Developer/client.html.twig', [ 'form' => $clientForm->createView(), ]); } @@ -80,7 +81,7 @@ class DeveloperController extends Controller $this->get('session')->getFlashBag()->add( 'notice', - 'flashes.developer.notice.client_deleted' + $this->get('translator')->trans('flashes.developer.notice.client_deleted', ['%name%' => $client->getName()]) ); return $this->redirect($this->generateUrl('developer')); @@ -95,6 +96,6 @@ class DeveloperController extends Controller */ public function howtoFirstAppAction() { - return $this->render('WallabagCoreBundle:Developer:howto_app.html.twig'); + return $this->render('@WallabagCore/themes/common/Developer/howto_app.html.twig'); } } diff --git a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php index af24e4988..9997913d2 100644 --- a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php +++ b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php @@ -7,7 +7,7 @@ use Hateoas\Configuration\Route; use Hateoas\Representation\Factory\PagerfantaFactory; use Nelmio\ApiDocBundle\Annotation\ApiDoc; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Wallabag\CoreBundle\Entity\Entry; @@ -22,6 +22,58 @@ class WallabagRestController extends FOSRestController } } + /** + * Check if an entry exist by url. + * + * @ApiDoc( + * parameters={ + * {"name"="url", "dataType"="string", "required"=true, "format"="An url", "description"="Url to check if it exists"}, + * {"name"="urls", "dataType"="string", "required"=false, "format"="An array of urls (?urls[]=http...&urls[]=http...)", "description"="Urls (as an array) to check if it exists"} + * } + * ) + * + * @return JsonResponse + */ + public function getEntriesExistsAction(Request $request) + { + $this->validateAuthentication(); + + $urls = $request->query->get('urls', []); + + // handle multiple urls first + if (!empty($urls)) { + $results = []; + foreach ($urls as $url) { + $res = $this->getDoctrine() + ->getRepository('WallabagCoreBundle:Entry') + ->findByUrlAndUserId($url, $this->getUser()->getId()); + + $results[$url] = false === $res ? false : true; + } + + $json = $this->get('serializer')->serialize($results, 'json'); + + return (new JsonResponse())->setJson($json); + } + + // let's see if it is a simple url? + $url = $request->query->get('url', ''); + + if (empty($url)) { + throw $this->createAccessDeniedException('URL is empty?, logged user id: '.$this->getUser()->getId()); + } + + $res = $this->getDoctrine() + ->getRepository('WallabagCoreBundle:Entry') + ->findByUrlAndUserId($url, $this->getUser()->getId()); + + $exists = false === $res ? false : true; + + $json = $this->get('serializer')->serialize(['exists' => $exists], 'json'); + + return (new JsonResponse())->setJson($json); + } + /** * Retrieve all entries. It could be filtered by many options. * @@ -34,10 +86,11 @@ class WallabagRestController extends FOSRestController * {"name"="page", "dataType"="integer", "required"=false, "format"="default '1'", "description"="what page you want."}, * {"name"="perPage", "dataType"="integer", "required"=false, "format"="default'30'", "description"="results per page."}, * {"name"="tags", "dataType"="string", "required"=false, "format"="api,rest", "description"="a list of tags url encoded. Will returns entries that matches ALL tags."}, + * {"name"="since", "dataType"="integer", "required"=false, "format"="default '0'", "description"="The timestamp since when you want entries updated."}, * } * ) * - * @return Response + * @return JsonResponse */ public function getEntriesAction(Request $request) { @@ -49,10 +102,12 @@ class WallabagRestController extends FOSRestController $order = $request->query->get('order', 'desc'); $page = (int) $request->query->get('page', 1); $perPage = (int) $request->query->get('perPage', 30); + $tags = $request->query->get('tags', ''); + $since = $request->query->get('since', 0); $pager = $this->getDoctrine() ->getRepository('WallabagCoreBundle:Entry') - ->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $sort, $order); + ->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $sort, $order, $since, $tags); $pager->setCurrentPage($page); $pager->setMaxPerPage($perPage); @@ -60,12 +115,25 @@ class WallabagRestController extends FOSRestController $pagerfantaFactory = new PagerfantaFactory('page', 'perPage'); $paginatedCollection = $pagerfantaFactory->createRepresentation( $pager, - new Route('api_get_entries', [], UrlGeneratorInterface::ABSOLUTE_URL) + new Route( + 'api_get_entries', + [ + 'archive' => $isArchived, + 'starred' => $isStarred, + 'sort' => $sort, + 'order' => $order, + 'page' => $page, + 'perPage' => $perPage, + 'tags' => $tags, + 'since' => $since, + ], + UrlGeneratorInterface::ABSOLUTE_URL + ) ); $json = $this->get('serializer')->serialize($paginatedCollection, 'json'); - return $this->renderJsonResponse($json); + return (new JsonResponse())->setJson($json); } /** @@ -77,7 +145,7 @@ class WallabagRestController extends FOSRestController * } * ) * - * @return Response + * @return JsonResponse */ public function getEntryAction(Entry $entry) { @@ -86,7 +154,7 @@ class WallabagRestController extends FOSRestController $json = $this->get('serializer')->serialize($entry, 'json'); - return $this->renderJsonResponse($json); + return (new JsonResponse())->setJson($json); } /** @@ -102,7 +170,7 @@ class WallabagRestController extends FOSRestController * } * ) * - * @return Response + * @return JsonResponse */ public function postEntriesAction(Request $request) { @@ -146,7 +214,7 @@ class WallabagRestController extends FOSRestController $json = $this->get('serializer')->serialize($entry, 'json'); - return $this->renderJsonResponse($json); + return (new JsonResponse())->setJson($json); } /** @@ -164,7 +232,7 @@ class WallabagRestController extends FOSRestController * } * ) * - * @return Response + * @return JsonResponse */ public function patchEntriesAction(Entry $entry, Request $request) { @@ -197,7 +265,7 @@ class WallabagRestController extends FOSRestController $json = $this->get('serializer')->serialize($entry, 'json'); - return $this->renderJsonResponse($json); + return (new JsonResponse())->setJson($json); } /** @@ -209,7 +277,7 @@ class WallabagRestController extends FOSRestController * } * ) * - * @return Response + * @return JsonResponse */ public function deleteEntriesAction(Entry $entry) { @@ -222,7 +290,7 @@ class WallabagRestController extends FOSRestController $json = $this->get('serializer')->serialize($entry, 'json'); - return $this->renderJsonResponse($json); + return (new JsonResponse())->setJson($json); } /** @@ -234,7 +302,7 @@ class WallabagRestController extends FOSRestController * } * ) * - * @return Response + * @return JsonResponse */ public function getEntriesTagsAction(Entry $entry) { @@ -243,7 +311,7 @@ class WallabagRestController extends FOSRestController $json = $this->get('serializer')->serialize($entry->getTags(), 'json'); - return $this->renderJsonResponse($json); + return (new JsonResponse())->setJson($json); } /** @@ -258,7 +326,7 @@ class WallabagRestController extends FOSRestController * } * ) * - * @return Response + * @return JsonResponse */ public function postEntriesTagsAction(Request $request, Entry $entry) { @@ -276,7 +344,7 @@ class WallabagRestController extends FOSRestController $json = $this->get('serializer')->serialize($entry, 'json'); - return $this->renderJsonResponse($json); + return (new JsonResponse())->setJson($json); } /** @@ -289,7 +357,7 @@ class WallabagRestController extends FOSRestController * } * ) * - * @return Response + * @return JsonResponse */ public function deleteEntriesTagsAction(Entry $entry, Tag $tag) { @@ -303,7 +371,7 @@ class WallabagRestController extends FOSRestController $json = $this->get('serializer')->serialize($entry, 'json'); - return $this->renderJsonResponse($json); + return (new JsonResponse())->setJson($json); } /** @@ -311,7 +379,7 @@ class WallabagRestController extends FOSRestController * * @ApiDoc() * - * @return Response + * @return JsonResponse */ public function getTagsAction() { @@ -323,7 +391,82 @@ class WallabagRestController extends FOSRestController $json = $this->get('serializer')->serialize($tags, 'json'); - return $this->renderJsonResponse($json); + return (new JsonResponse())->setJson($json); + } + + /** + * Permanently remove one tag from **every** entry. + * + * @ApiDoc( + * requirements={ + * {"name"="tag", "dataType"="string", "required"=true, "requirement"="\w+", "description"="Tag as a string"} + * } + * ) + * + * @return JsonResponse + */ + public function deleteTagLabelAction(Request $request) + { + $this->validateAuthentication(); + $label = $request->request->get('tag', ''); + + $tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($label); + + if (empty($tag)) { + throw $this->createNotFoundException('Tag not found'); + } + + $this->getDoctrine() + ->getRepository('WallabagCoreBundle:Entry') + ->removeTag($this->getUser()->getId(), $tag); + + $this->cleanOrphanTag($tag); + + $json = $this->get('serializer')->serialize($tag, 'json'); + + return (new JsonResponse())->setJson($json); + } + + /** + * Permanently remove some tags from **every** entry. + * + * @ApiDoc( + * requirements={ + * {"name"="tags", "dataType"="string", "required"=true, "format"="tag1,tag2", "description"="Tags as strings (comma splitted)"} + * } + * ) + * + * @return JsonResponse + */ + public function deleteTagsLabelAction(Request $request) + { + $this->validateAuthentication(); + + $tagsLabels = $request->request->get('tags', ''); + + $tags = []; + + foreach (explode(',', $tagsLabels) as $tagLabel) { + $tagEntity = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($tagLabel); + + if (!empty($tagEntity)) { + $tags[] = $tagEntity; + } + } + + if (empty($tags)) { + throw $this->createNotFoundException('Tags not found'); + } + + $this->getDoctrine() + ->getRepository('WallabagCoreBundle:Entry') + ->removeTags($this->getUser()->getId(), $tags); + + $this->cleanOrphanTag($tags); + + $json = $this->get('serializer')->serialize($tags, 'json'); + + return (new JsonResponse())->setJson($json); } /** @@ -335,7 +478,7 @@ class WallabagRestController extends FOSRestController * } * ) * - * @return Response + * @return JsonResponse */ public function deleteTagAction(Tag $tag) { @@ -345,16 +488,19 @@ class WallabagRestController extends FOSRestController ->getRepository('WallabagCoreBundle:Entry') ->removeTag($this->getUser()->getId(), $tag); + $this->cleanOrphanTag($tag); + $json = $this->get('serializer')->serialize($tag, 'json'); - return $this->renderJsonResponse($json); + return (new JsonResponse())->setJson($json); } + /** * Retrieve version number. * * @ApiDoc() * - * @return Response + * @return JsonResponse */ public function getVersionAction() { @@ -362,7 +508,29 @@ class WallabagRestController extends FOSRestController $json = $this->get('serializer')->serialize($version, 'json'); - return $this->renderJsonResponse($json); + return (new JsonResponse())->setJson($json); + } + + /** + * Remove orphan tag in case no entries are associated to it. + * + * @param Tag|array $tags + */ + private function cleanOrphanTag($tags) + { + if (!is_array($tags)) { + $tags = [$tags]; + } + + $em = $this->getDoctrine()->getManager(); + + foreach ($tags as $tag) { + if (count($tag->getEntries()) === 0) { + $em->remove($tag); + } + } + + $em->flush(); } /** @@ -378,17 +546,4 @@ class WallabagRestController extends FOSRestController throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$requestUserId.', logged user id: '.$user->getId()); } } - - /** - * Send a JSON Response. - * We don't use the Symfony JsonRespone, because it takes an array as parameter instead of a JSON string. - * - * @param string $json - * - * @return Response - */ - private function renderJsonResponse($json) - { - return new Response($json, 200, ['application/json']); - } } diff --git a/src/Wallabag/ApiBundle/Entity/AccessToken.php b/src/Wallabag/ApiBundle/Entity/AccessToken.php index 2ff63a83e..c09a0c803 100644 --- a/src/Wallabag/ApiBundle/Entity/AccessToken.php +++ b/src/Wallabag/ApiBundle/Entity/AccessToken.php @@ -19,7 +19,7 @@ class AccessToken extends BaseAccessToken protected $id; /** - * @ORM\ManyToOne(targetEntity="Client") + * @ORM\ManyToOne(targetEntity="Client", inversedBy="accessTokens") * @ORM\JoinColumn(nullable=false) */ protected $client; diff --git a/src/Wallabag/ApiBundle/Entity/Client.php b/src/Wallabag/ApiBundle/Entity/Client.php index c04ed0f62..f7898ac82 100644 --- a/src/Wallabag/ApiBundle/Entity/Client.php +++ b/src/Wallabag/ApiBundle/Entity/Client.php @@ -18,8 +18,49 @@ class Client extends BaseClient */ protected $id; + /** + * @var string + * + * @ORM\Column(name="name", type="text", nullable=true) + */ + protected $name; + + /** + * @ORM\OneToMany(targetEntity="RefreshToken", mappedBy="client", cascade={"remove"}) + */ + protected $refreshTokens; + + /** + * @ORM\OneToMany(targetEntity="AccessToken", mappedBy="client", cascade={"remove"}) + */ + protected $accessTokens; + public function __construct() { parent::__construct(); } + + /** + * Get name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Set name. + * + * @param string $name + * + * @return Client + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } } diff --git a/src/Wallabag/ApiBundle/Entity/RefreshToken.php b/src/Wallabag/ApiBundle/Entity/RefreshToken.php index 6d0cab688..822a02d8d 100644 --- a/src/Wallabag/ApiBundle/Entity/RefreshToken.php +++ b/src/Wallabag/ApiBundle/Entity/RefreshToken.php @@ -19,7 +19,7 @@ class RefreshToken extends BaseRefreshToken protected $id; /** - * @ORM\ManyToOne(targetEntity="Client") + * @ORM\ManyToOne(targetEntity="Client", inversedBy="refreshTokens") * @ORM\JoinColumn(nullable=false) */ protected $client; diff --git a/src/Wallabag/CoreBundle/Form/Type/ClientType.php b/src/Wallabag/ApiBundle/Form/Type/ClientType.php similarity index 82% rename from src/Wallabag/CoreBundle/Form/Type/ClientType.php rename to src/Wallabag/ApiBundle/Form/Type/ClientType.php index 8b351e603..0ea1a9c5c 100644 --- a/src/Wallabag/CoreBundle/Form/Type/ClientType.php +++ b/src/Wallabag/ApiBundle/Form/Type/ClientType.php @@ -1,11 +1,12 @@ add('redirect_uris', UrlType::class, ['required' => true, 'label' => 'developer.client.form.redirect_uris_label']) + ->add('name', TextType::class, ['label' => 'developer.client.form.name_label']) + ->add('redirect_uris', UrlType::class, ['required' => false, 'label' => 'developer.client.form.redirect_uris_label']) ->add('save', SubmitType::class, ['label' => 'developer.client.form.save_label']) ; diff --git a/src/Wallabag/CoreBundle/Command/InstallCommand.php b/src/Wallabag/CoreBundle/Command/InstallCommand.php index 035eb8655..8e438229f 100644 --- a/src/Wallabag/CoreBundle/Command/InstallCommand.php +++ b/src/Wallabag/CoreBundle/Command/InstallCommand.php @@ -2,12 +2,14 @@ namespace Wallabag\CoreBundle\Command; +use FOS\UserBundle\Event\UserEvent; +use FOS\UserBundle\FOSUserEvents; use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Output\BufferedOutput; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\ConfirmationQuestion; use Symfony\Component\Console\Question\Question; @@ -61,7 +63,6 @@ class InstallCommand extends ContainerAwareCommand ->setupDatabase() ->setupAdmin() ->setupConfig() - ->setupAsset() ; $output->writeln('Wallabag has been successfully installed.'); @@ -70,7 +71,7 @@ class InstallCommand extends ContainerAwareCommand protected function checkRequirements() { - $this->defaultOutput->writeln('Step 1 of 5. Checking system requirements.'); + $this->defaultOutput->writeln('Step 1 of 4. Checking system requirements.'); $rows = []; @@ -96,7 +97,8 @@ class InstallCommand extends ContainerAwareCommand try { $this->getContainer()->get('doctrine')->getManager()->getConnection()->connect(); } catch (\Exception $e) { - if (false === strpos($e->getMessage(), 'Unknown database')) { + if (false === strpos($e->getMessage(), 'Unknown database') + && false === strpos($e->getMessage(), 'database "'.$this->getContainer()->getParameter('database_name').'" does not exist')) { $fulfilled = false; $status = 'ERROR!'; $help = 'Can\'t connect to the database: '.$e->getMessage(); @@ -138,7 +140,7 @@ class InstallCommand extends ContainerAwareCommand protected function setupDatabase() { - $this->defaultOutput->writeln('Step 2 of 5. Setting up database.'); + $this->defaultOutput->writeln('Step 2 of 4. Setting up database.'); // user want to reset everything? Don't care about what is already here if (true === $this->defaultInput->getOption('reset')) { @@ -209,7 +211,7 @@ class InstallCommand extends ContainerAwareCommand protected function setupAdmin() { - $this->defaultOutput->writeln('Step 3 of 5. Administration setup.'); + $this->defaultOutput->writeln('Step 3 of 4. Administration setup.'); $questionHelper = $this->getHelperSet()->get('question'); $question = new ConfirmationQuestion('Would you like to create a new admin user (recommended) ? (Y/n)', true); @@ -237,14 +239,9 @@ class InstallCommand extends ContainerAwareCommand $em->persist($user); - $config = new Config($user); - $config->setTheme($this->getContainer()->getParameter('wallabag_core.theme')); - $config->setItemsPerPage($this->getContainer()->getParameter('wallabag_core.items_on_page')); - $config->setRssLimit($this->getContainer()->getParameter('wallabag_core.rss_limit')); - $config->setReadingSpeed($this->getContainer()->getParameter('wallabag_core.reading_speed')); - $config->setLanguage($this->getContainer()->getParameter('wallabag_core.language')); - - $em->persist($config); + // dispatch a created event so the associated config will be created + $event = new UserEvent($user); + $this->getContainer()->get('event_dispatcher')->dispatch(FOSUserEvents::USER_CREATED, $event); $this->defaultOutput->writeln(''); @@ -253,13 +250,18 @@ class InstallCommand extends ContainerAwareCommand protected function setupConfig() { - $this->defaultOutput->writeln('Step 4 of 5. Config setup.'); + $this->defaultOutput->writeln('Step 4 of 4. Config setup.'); $em = $this->getContainer()->get('doctrine.orm.entity_manager'); // cleanup before insert new stuff $em->createQuery('DELETE FROM CraueConfigBundle:Setting')->execute(); $settings = [ + [ + 'name' => 'share_public', + 'value' => '1', + 'section' => 'entry', + ], [ 'name' => 'carrot', 'value' => '1', @@ -331,8 +333,13 @@ class InstallCommand extends ContainerAwareCommand 'section' => 'export', ], [ - 'name' => 'pocket_consumer_key', - 'value' => null, + 'name' => 'import_with_redis', + 'value' => '0', + 'section' => 'import', + ], + [ + 'name' => 'import_with_rabbitmq', + 'value' => '0', 'section' => 'import', ], [ @@ -392,20 +399,6 @@ class InstallCommand extends ContainerAwareCommand return $this; } - protected function setupAsset() - { - $this->defaultOutput->writeln('Step 5 of 5. Installing assets.'); - - $this - ->runCommand('assets:install') - ->runCommand('assetic:dump') - ; - - $this->defaultOutput->writeln(''); - - return $this; - } - /** * Run a command. * @@ -428,16 +421,18 @@ class InstallCommand extends ContainerAwareCommand } $this->getApplication()->setAutoExit(false); - $exitCode = $this->getApplication()->run(new ArrayInput($parameters), new NullOutput()); + + $output = new BufferedOutput(); + $exitCode = $this->getApplication()->run(new ArrayInput($parameters), $output); if (0 !== $exitCode) { $this->getApplication()->setAutoExit(true); - $errorMessage = sprintf('The command "%s" terminated with an error code: %u.', $command, $exitCode); - $this->defaultOutput->writeln("$errorMessage"); - $exception = new \Exception($errorMessage, $exitCode); + $this->defaultOutput->writeln(''); + $this->defaultOutput->writeln('The command "'.$command.'" generates some errors: '); + $this->defaultOutput->writeln($output->fetch()); - throw $exception; + die(); } // PDO does not always close the connection after Doctrine commands. diff --git a/src/Wallabag/CoreBundle/Command/TagAllCommand.php b/src/Wallabag/CoreBundle/Command/TagAllCommand.php index db1a9ab79..3f9bb04d9 100644 --- a/src/Wallabag/CoreBundle/Command/TagAllCommand.php +++ b/src/Wallabag/CoreBundle/Command/TagAllCommand.php @@ -34,10 +34,13 @@ class TagAllCommand extends ContainerAwareCommand } $tagger = $this->getContainer()->get('wallabag_core.rule_based_tagger'); - $output->write(sprintf('Tagging entries for user « %s »... ', $user->getUserName())); + $output->write(sprintf('Tagging entries for user « %s »... ', $user->getUserName())); $entries = $tagger->tagAllForUser($user); + $output->writeln('Done.'); + $output->write(sprintf('Persist entries ... ', $user->getUserName())); + $em = $this->getDoctrine()->getManager(); foreach ($entries as $entry) { $em->persist($entry); diff --git a/src/Wallabag/CoreBundle/Controller/ConfigController.php b/src/Wallabag/CoreBundle/Controller/ConfigController.php index 4f75511bf..91cdcae50 100644 --- a/src/Wallabag/CoreBundle/Controller/ConfigController.php +++ b/src/Wallabag/CoreBundle/Controller/ConfigController.php @@ -11,7 +11,6 @@ use Wallabag\CoreBundle\Entity\Config; use Wallabag\CoreBundle\Entity\TaggingRule; use Wallabag\CoreBundle\Form\Type\ConfigType; use Wallabag\CoreBundle\Form\Type\ChangePasswordType; -use Wallabag\CoreBundle\Form\Type\NewUserType; use Wallabag\CoreBundle\Form\Type\RssType; use Wallabag\CoreBundle\Form\Type\TaggingRuleType; use Wallabag\CoreBundle\Form\Type\UserInformationType; @@ -106,7 +105,21 @@ class ConfigController extends Controller // handle tagging rule $taggingRule = new TaggingRule(); - $newTaggingRule = $this->createForm(TaggingRuleType::class, $taggingRule, ['action' => $this->generateUrl('config').'#set5']); + $action = $this->generateUrl('config').'#set5'; + + if ($request->query->has('tagging-rule')) { + $taggingRule = $this->getDoctrine() + ->getRepository('WallabagCoreBundle:TaggingRule') + ->find($request->query->get('tagging-rule')); + + if ($this->getUser()->getId() !== $taggingRule->getConfig()->getUser()->getId()) { + return $this->redirect($action); + } + + $action = $this->generateUrl('config').'?tagging-rule='.$taggingRule->getId().'#set5'; + } + + $newTaggingRule = $this->createForm(TaggingRuleType::class, $taggingRule, ['action' => $action]); $newTaggingRule->handleRequest($request); if ($newTaggingRule->isValid()) { @@ -122,45 +135,12 @@ class ConfigController extends Controller return $this->redirect($this->generateUrl('config').'#set5'); } - // handle adding new user - $newUser = $userManager->createUser(); - // enable created user by default - $newUser->setEnabled(true); - $newUserForm = $this->createForm(NewUserType::class, $newUser, [ - 'validation_groups' => ['Profile'], - 'action' => $this->generateUrl('config').'#set6', - ]); - $newUserForm->handleRequest($request); - - if ($newUserForm->isValid() && $this->get('security.authorization_checker')->isGranted('ROLE_SUPER_ADMIN')) { - $userManager->updateUser($newUser, true); - - $config = new Config($newUser); - $config->setTheme($this->getParameter('wallabag_core.theme')); - $config->setItemsPerPage($this->getParameter('wallabag_core.items_on_page')); - $config->setRssLimit($this->getParameter('wallabag_core.rss_limit')); - $config->setLanguage($this->getParameter('wallabag_core.language')); - $config->setReadingSpeed($this->getParameter('wallabag_core.reading_speed')); - - $em->persist($config); - - $em->flush(); - - $this->get('session')->getFlashBag()->add( - 'notice', - $this->get('translator')->trans('flashes.config.notice.user_added', ['%username%' => $newUser->getUsername()]) - ); - - return $this->redirect($this->generateUrl('config').'#set6'); - } - return $this->render('WallabagCoreBundle:Config:index.html.twig', [ 'form' => [ 'config' => $configForm->createView(), 'rss' => $rssForm->createView(), 'pwd' => $pwdForm->createView(), 'user' => $userForm->createView(), - 'new_user' => $newUserForm->createView(), 'new_tagging_rule' => $newTaggingRule->createView(), ], 'rss' => [ @@ -210,9 +190,7 @@ class ConfigController extends Controller */ public function deleteTaggingRuleAction(TaggingRule $rule) { - if ($this->getUser()->getId() != $rule->getConfig()->getUser()->getId()) { - throw $this->createAccessDeniedException('You can not access this tagging rule.'); - } + $this->validateRuleAction($rule); $em = $this->getDoctrine()->getManager(); $em->remove($rule); @@ -226,6 +204,34 @@ class ConfigController extends Controller return $this->redirect($this->generateUrl('config').'#set5'); } + /** + * Edit a tagging rule. + * + * @param TaggingRule $rule + * + * @Route("/tagging-rule/edit/{id}", requirements={"id" = "\d+"}, name="edit_tagging_rule") + * + * @return RedirectResponse + */ + public function editTaggingRuleAction(TaggingRule $rule) + { + $this->validateRuleAction($rule); + + return $this->redirect($this->generateUrl('config').'?tagging-rule='.$rule->getId().'#set5'); + } + + /** + * Validate that a rule can be edited/deleted by the current user. + * + * @param TaggingRule $rule + */ + private function validateRuleAction(TaggingRule $rule) + { + if ($this->getUser()->getId() != $rule->getConfig()->getUser()->getId()) { + throw $this->createAccessDeniedException('You can not access this tagging rule.'); + } + } + /** * Retrieve config for the current user. * If no config were found, create a new one. @@ -238,6 +244,7 @@ class ConfigController extends Controller ->getRepository('WallabagCoreBundle:Config') ->findOneByUser($this->getUser()); + // should NEVER HAPPEN ... if (!$config) { $config = new Config($this->getUser()); } diff --git a/src/Wallabag/CoreBundle/Controller/EntryController.php b/src/Wallabag/CoreBundle/Controller/EntryController.php index ccdf94066..3b28e6350 100644 --- a/src/Wallabag/CoreBundle/Controller/EntryController.php +++ b/src/Wallabag/CoreBundle/Controller/EntryController.php @@ -4,7 +4,6 @@ namespace Wallabag\CoreBundle\Controller; use Pagerfanta\Adapter\DoctrineORMAdapter; use Pagerfanta\Exception\OutOfRangeCurrentPageException; -use Pagerfanta\Pagerfanta; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; @@ -13,30 +12,40 @@ use Wallabag\CoreBundle\Entity\Entry; use Wallabag\CoreBundle\Form\Type\EntryFilterType; use Wallabag\CoreBundle\Form\Type\EditEntryType; use Wallabag\CoreBundle\Form\Type\NewEntryType; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache; class EntryController extends Controller { /** - * @param Entry $entry + * Fetch content and update entry. + * In case it fails, entry will return to avod loosing the data. + * + * @param Entry $entry + * @param string $prefixMessage Should be the translation key: entry_saved or entry_reloaded + * + * @return Entry */ - private function updateEntry(Entry $entry) + private function updateEntry(Entry $entry, $prefixMessage = 'entry_saved') { + // put default title in case of fetching content failed + $entry->setTitle('No title found'); + + $message = 'flashes.entry.notice.'.$prefixMessage; + try { $entry = $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl()); - - $em = $this->getDoctrine()->getManager(); - $em->persist($entry); - $em->flush(); } catch (\Exception $e) { $this->get('logger')->error('Error while saving an entry', [ 'exception' => $e, 'entry' => $entry, ]); - return false; + $message = 'flashes.entry.notice.'.$prefixMessage.'_failed'; } - return true; + $this->get('session')->getFlashBag()->add('notice', $message); + + return $entry; } /** @@ -66,12 +75,11 @@ class EntryController extends Controller return $this->redirect($this->generateUrl('view', ['id' => $existingEntry->getId()])); } - $message = 'flashes.entry.notice.entry_saved'; - if (false === $this->updateEntry($entry)) { - $message = 'flashes.entry.notice.entry_saved_failed'; - } + $this->updateEntry($entry); - $this->get('session')->getFlashBag()->add('notice', $message); + $em = $this->getDoctrine()->getManager(); + $em->persist($entry); + $em->flush(); return $this->redirect($this->generateUrl('homepage')); } @@ -95,6 +103,10 @@ class EntryController extends Controller if (false === $this->checkIfEntryAlreadyExists($entry)) { $this->updateEntry($entry); + + $em = $this->getDoctrine()->getManager(); + $em->persist($entry); + $em->flush(); } return $this->redirect($this->generateUrl('homepage')); @@ -226,6 +238,10 @@ class EntryController extends Controller $repository = $this->get('wallabag_core.entry_repository'); switch ($type) { + case 'untagged': + $qb = $repository->getBuilderForUntaggedByUser($this->getUser()->getId()); + + break; case 'starred': $qb = $repository->getBuilderForStarredByUser($this->getUser()->getId()); break; @@ -257,9 +273,10 @@ class EntryController extends Controller } $pagerAdapter = new DoctrineORMAdapter($qb->getQuery()); - $entries = new Pagerfanta($pagerAdapter); - $entries->setMaxPerPage($this->getUser()->getConfig()->getItemsPerPage()); + $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries') + ->prepare($pagerAdapter, $page); + try { $entries->setCurrentPage($page); } catch (OutOfRangeCurrentPageException $e) { @@ -311,15 +328,11 @@ class EntryController extends Controller { $this->checkUserAction($entry); - $message = 'flashes.entry.notice.entry_reloaded'; - if (false === $this->updateEntry($entry)) { - $message = 'flashes.entry.notice.entry_reload_failed'; - } + $this->updateEntry($entry, 'entry_reloaded'); - $this->get('session')->getFlashBag()->add( - 'notice', - $message - ); + $em = $this->getDoctrine()->getManager(); + $em->persist($entry); + $em->flush(); return $this->redirect($this->generateUrl('view', ['id' => $entry->getId()])); } @@ -434,7 +447,7 @@ class EntryController extends Controller */ private function checkUserAction(Entry $entry) { - if ($this->getUser()->getId() != $entry->getUser()->getId()) { + if (null === $this->getUser() || $this->getUser()->getId() != $entry->getUser()->getId()) { throw $this->createAccessDeniedException('You can not access this entry.'); } } @@ -450,4 +463,91 @@ class EntryController extends Controller { return $this->get('wallabag_core.entry_repository')->findByUrlAndUserId($entry->getUrl(), $this->getUser()->getId()); } + + /** + * Get public URL for entry (and generate it if necessary). + * + * @param Entry $entry + * + * @Route("/share/{id}", requirements={"id" = "\d+"}, name="share") + * + * @return \Symfony\Component\HttpFoundation\Response + */ + public function shareAction(Entry $entry) + { + $this->checkUserAction($entry); + + if (null === $entry->getUuid()) { + $entry->generateUuid(); + + $em = $this->getDoctrine()->getManager(); + $em->persist($entry); + $em->flush(); + } + + return $this->redirect($this->generateUrl('share_entry', [ + 'uuid' => $entry->getUuid(), + ])); + } + + /** + * Disable public sharing for an entry. + * + * @param Entry $entry + * + * @Route("/share/delete/{id}", requirements={"id" = "\d+"}, name="delete_share") + * + * @return \Symfony\Component\HttpFoundation\Response + */ + public function deleteShareAction(Entry $entry) + { + $this->checkUserAction($entry); + + $entry->cleanUuid(); + + $em = $this->getDoctrine()->getManager(); + $em->persist($entry); + $em->flush(); + + return $this->redirect($this->generateUrl('view', [ + 'id' => $entry->getId(), + ])); + } + + /** + * Ability to view a content publicly. + * + * @param Entry $entry + * + * @Route("/share/{uuid}", requirements={"uuid" = ".+"}, name="share_entry") + * @Cache(maxage="25200", smaxage="25200", public=true) + * + * @return \Symfony\Component\HttpFoundation\Response + */ + public function shareEntryAction(Entry $entry) + { + if (!$this->get('craue_config')->get('share_public')) { + throw $this->createAccessDeniedException('Sharing an entry is disabled for this user.'); + } + + return $this->render( + '@WallabagCore/themes/common/Entry/share.html.twig', + ['entry' => $entry] + ); + } + + /** + * Shows untagged articles for current user. + * + * @param Request $request + * @param int $page + * + * @Route("/untagged/list/{page}", name="untagged", defaults={"page" = "1"}) + * + * @return \Symfony\Component\HttpFoundation\Response + */ + public function showUntaggedEntriesAction(Request $request, $page) + { + return $this->showEntries('untagged', $request, $page); + } } diff --git a/src/Wallabag/CoreBundle/Controller/ExceptionController.php b/src/Wallabag/CoreBundle/Controller/ExceptionController.php new file mode 100644 index 000000000..abfa9c2ff --- /dev/null +++ b/src/Wallabag/CoreBundle/Controller/ExceptionController.php @@ -0,0 +1,40 @@ +templateExists($template)) { + return $template; + } + } + + // try to find a template for the given format + $template = sprintf('@Twig/Exception/%s.%s.twig', $name, $format); + if ($this->templateExists($template)) { + return $template; + } + + // default to a generic HTML exception + $request->setRequestFormat('html'); + + return sprintf('@Twig/Exception/%s.html.twig', $showException ? 'exception_full' : $name); + } +} diff --git a/src/Wallabag/CoreBundle/Controller/ExportController.php b/src/Wallabag/CoreBundle/Controller/ExportController.php index 944c755de..6191d5d79 100644 --- a/src/Wallabag/CoreBundle/Controller/ExportController.php +++ b/src/Wallabag/CoreBundle/Controller/ExportController.php @@ -46,7 +46,7 @@ class ExportController extends Controller * * @Route("/export/{category}.{format}", name="export_entries", requirements={ * "format": "epub|mobi|pdf|json|xml|txt|csv", - * "category": "all|unread|starred|archive" + * "category": "all|unread|starred|archive|tag_entries|untagged" * }) * * @return \Symfony\Component\HttpFoundation\Response diff --git a/src/Wallabag/CoreBundle/Controller/FooterController.php b/src/Wallabag/CoreBundle/Controller/FooterController.php deleted file mode 100644 index fd93c436f..000000000 --- a/src/Wallabag/CoreBundle/Controller/FooterController.php +++ /dev/null @@ -1,27 +0,0 @@ -container->getParameter('addons_url'); - $socialsUrl = $this->container->getParameter('socials_url'); - - return $this->render( - 'WallabagCoreBundle::footer.html.twig', - [ - 'addonsUrl' => $addonsUrl, - 'socialsUrl' => $socialsUrl, - ] - ); - } -} diff --git a/src/Wallabag/CoreBundle/Controller/RssController.php b/src/Wallabag/CoreBundle/Controller/RssController.php index 2118885c1..38e3b5a0e 100644 --- a/src/Wallabag/CoreBundle/Controller/RssController.php +++ b/src/Wallabag/CoreBundle/Controller/RssController.php @@ -87,7 +87,7 @@ class RssController extends Controller $perPage = $user->getConfig()->getRssLimit() ?: $this->getParameter('wallabag_core.rss_limit'); $entries->setMaxPerPage($perPage); - return $this->render('WallabagCoreBundle:Entry:entries.xml.twig', [ + return $this->render('@WallabagCore/themes/common/Entry/entries.xml.twig', [ 'type' => $type, 'entries' => $entries, ]); diff --git a/src/Wallabag/CoreBundle/Controller/StaticController.php b/src/Wallabag/CoreBundle/Controller/StaticController.php index 2a57f06fb..82714217b 100644 --- a/src/Wallabag/CoreBundle/Controller/StaticController.php +++ b/src/Wallabag/CoreBundle/Controller/StaticController.php @@ -15,7 +15,7 @@ class StaticController extends Controller $addonsUrl = $this->container->getParameter('addons_url'); return $this->render( - 'WallabagCoreBundle:Static:howto.html.twig', + '@WallabagCore/themes/common/Static/howto.html.twig', ['addonsUrl' => $addonsUrl] ); } @@ -26,7 +26,7 @@ class StaticController extends Controller public function aboutAction() { return $this->render( - 'WallabagCoreBundle:Static:about.html.twig', + '@WallabagCore/themes/common/Static/about.html.twig', [ 'version' => $this->getParameter('wallabag_core.version'), 'paypal_url' => $this->getParameter('wallabag_core.paypal_url'), @@ -40,7 +40,7 @@ class StaticController extends Controller public function quickstartAction() { return $this->render( - 'WallabagCoreBundle:Static:quickstart.html.twig', + '@WallabagCore/themes/common/Static/quickstart.html.twig', [] ); } diff --git a/src/Wallabag/CoreBundle/Controller/TagController.php b/src/Wallabag/CoreBundle/Controller/TagController.php index 8645fb442..5acc68528 100644 --- a/src/Wallabag/CoreBundle/Controller/TagController.php +++ b/src/Wallabag/CoreBundle/Controller/TagController.php @@ -2,12 +2,15 @@ namespace Wallabag\CoreBundle\Controller; +use Pagerfanta\Adapter\ArrayAdapter; +use Pagerfanta\Exception\OutOfRangeCurrentPageException; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; use Wallabag\CoreBundle\Entity\Entry; use Wallabag\CoreBundle\Entity\Tag; use Wallabag\CoreBundle\Form\Type\NewTagType; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; class TagController extends Controller { @@ -60,10 +63,12 @@ class TagController extends Controller $entry->removeTag($tag); $em = $this->getDoctrine()->getManager(); $em->flush(); - if (count($tag->getEntries()) == 0) { + + // remove orphan tag in case no entries are associated to it + if (count($tag->getEntries()) === 0) { $em->remove($tag); + $em->flush(); } - $em->flush(); $redirectUrl = $this->get('wallabag_core.helper.redirect')->to($request->headers->get('referer')); @@ -83,11 +88,61 @@ class TagController extends Controller ->getRepository('WallabagCoreBundle:Tag') ->findAllTags($this->getUser()->getId()); - return $this->render( - 'WallabagCoreBundle:Tag:tags.html.twig', - [ - 'tags' => $tags, - ] - ); + $flatTags = []; + + foreach ($tags as $key => $tag) { + $nbEntries = $this->getDoctrine() + ->getRepository('WallabagCoreBundle:Entry') + ->countAllEntriesByUserIdAndTagId($this->getUser()->getId(), $tag['id']); + + $flatTags[] = [ + 'id' => $tag['id'], + 'label' => $tag['label'], + 'slug' => $tag['slug'], + 'nbEntries' => $nbEntries, + ]; + } + + return $this->render('WallabagCoreBundle:Tag:tags.html.twig', [ + 'tags' => $flatTags, + ]); + } + + /** + * @param Tag $tag + * @param int $page + * + * @Route("/tag/list/{slug}/{page}", name="tag_entries", defaults={"page" = "1"}) + * @ParamConverter("tag", options={"mapping": {"slug": "slug"}}) + * + * @return \Symfony\Component\HttpFoundation\Response + */ + public function showEntriesForTagAction(Tag $tag, $page, Request $request) + { + $entriesByTag = $this->getDoctrine() + ->getRepository('WallabagCoreBundle:Entry') + ->findAllByTagId($this->getUser()->getId(), $tag->getId()); + + $pagerAdapter = new ArrayAdapter($entriesByTag); + + $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries') + ->prepare($pagerAdapter, $page); + + try { + $entries->setCurrentPage($page); + } catch (OutOfRangeCurrentPageException $e) { + if ($page > 1) { + return $this->redirect($this->generateUrl($request->get('_route'), [ + 'slug' => $tag->getSlug(), + 'page' => $entries->getNbPages(), + ]), 302); + } + } + + return $this->render('WallabagCoreBundle:Entry:entries.html.twig', [ + 'form' => null, + 'entries' => $entries, + 'currentPage' => $page, + ]); } } diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php index 03be96670..921c739ff 100644 --- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php +++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php @@ -20,6 +20,7 @@ class LoadConfigData extends AbstractFixture implements OrderedFixtureInterface $adminConfig->setItemsPerPage(30); $adminConfig->setReadingSpeed(1); $adminConfig->setLanguage('en'); + $adminConfig->setPocketConsumerKey('xxxxx'); $manager->persist($adminConfig); @@ -30,6 +31,7 @@ class LoadConfigData extends AbstractFixture implements OrderedFixtureInterface $bobConfig->setItemsPerPage(10); $bobConfig->setReadingSpeed(1); $bobConfig->setLanguage('fr'); + $bobConfig->setPocketConsumerKey(null); $manager->persist($bobConfig); @@ -40,6 +42,7 @@ class LoadConfigData extends AbstractFixture implements OrderedFixtureInterface $emptyConfig->setItemsPerPage(10); $emptyConfig->setReadingSpeed(1); $emptyConfig->setLanguage('en'); + $emptyConfig->setPocketConsumerKey(null); $manager->persist($emptyConfig); diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadEntryData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadEntryData.php index 6c6a331a6..fedad0099 100644 --- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadEntryData.php +++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadEntryData.php @@ -23,6 +23,9 @@ class LoadEntryData extends AbstractFixture implements OrderedFixtureInterface $entry1->setContent('This is my content /o/'); $entry1->setLanguage('en'); + $entry1->addTag($this->getReference('foo-tag')); + $entry1->addTag($this->getReference('baz-tag')); + $manager->persist($entry1); $this->addReference('entry1', $entry1); @@ -96,6 +99,7 @@ class LoadEntryData extends AbstractFixture implements OrderedFixtureInterface $entry6->setContent('This is my content /o/'); $entry6->setArchived(true); $entry6->setLanguage('de'); + $entry6->addTag($this->getReference('bar-tag')); $manager->persist($entry6); diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php index 090587961..9425f961f 100644 --- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php +++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php @@ -15,6 +15,11 @@ class LoadSettingData extends AbstractFixture implements OrderedFixtureInterface public function load(ObjectManager $manager) { $settings = [ + [ + 'name' => 'share_public', + 'value' => '1', + 'section' => 'entry', + ], [ 'name' => 'carrot', 'value' => '1', @@ -86,8 +91,13 @@ class LoadSettingData extends AbstractFixture implements OrderedFixtureInterface 'section' => 'export', ], [ - 'name' => 'pocket_consumer_key', - 'value' => null, + 'name' => 'import_with_redis', + 'value' => '0', + 'section' => 'import', + ], + [ + 'name' => 'import_with_rabbitmq', + 'value' => '0', 'section' => 'import', ], [ diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadTagData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadTagData.php index 8553dced4..09e99f36b 100644 --- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadTagData.php +++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadTagData.php @@ -28,6 +28,13 @@ class LoadTagData extends AbstractFixture implements OrderedFixtureInterface $this->addReference('bar-tag', $tag2); + $tag3 = new Tag(); + $tag3->setLabel('baz'); + + $manager->persist($tag3); + + $this->addReference('baz-tag', $tag3); + $manager->flush(); } diff --git a/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php b/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php index d1bb9820b..d8141eea9 100644 --- a/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php +++ b/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php @@ -36,6 +36,9 @@ class Configuration implements ConfigurationInterface ->end() ->scalarNode('paypal_url') ->end() + ->integerNode('cache_lifetime') + ->defaultValue(10) + ->end() ->end() ; diff --git a/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php b/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php index 7d08b73b7..0cbde908d 100644 --- a/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php +++ b/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php @@ -22,6 +22,7 @@ class WallabagCoreExtension extends Extension $container->setParameter('wallabag_core.reading_speed', $config['reading_speed']); $container->setParameter('wallabag_core.version', $config['version']); $container->setParameter('wallabag_core.paypal_url', $config['paypal_url']); + $container->setParameter('wallabag_core.cache_lifetime', $config['cache_lifetime']); $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('services.yml'); diff --git a/src/Wallabag/CoreBundle/Entity/Config.php b/src/Wallabag/CoreBundle/Entity/Config.php index a25656d3b..d0f0e3f38 100644 --- a/src/Wallabag/CoreBundle/Entity/Config.php +++ b/src/Wallabag/CoreBundle/Entity/Config.php @@ -80,6 +80,13 @@ class Config */ private $readingSpeed; + /** + * @var string + * + * @ORM\Column(name="pocket_consumer_key", type="string", nullable=true) + */ + private $pocketConsumerKey; + /** * @ORM\OneToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="config") */ @@ -278,6 +285,30 @@ class Config return $this->readingSpeed; } + /** + * Set pocketConsumerKey. + * + * @param string $pocketConsumerKey + * + * @return Config + */ + public function setPocketConsumerKey($pocketConsumerKey) + { + $this->pocketConsumerKey = $pocketConsumerKey; + + return $this; + } + + /** + * Get pocketConsumerKey. + * + * @return string + */ + public function getPocketConsumerKey() + { + return $this->pocketConsumerKey; + } + /** * @param TaggingRule $rule * diff --git a/src/Wallabag/CoreBundle/Entity/Entry.php b/src/Wallabag/CoreBundle/Entity/Entry.php index ceae78b05..f2da3f4de 100644 --- a/src/Wallabag/CoreBundle/Entity/Entry.php +++ b/src/Wallabag/CoreBundle/Entity/Entry.php @@ -37,6 +37,15 @@ class Entry */ private $id; + /** + * @var string + * + * @ORM\Column(name="uuid", type="text", nullable=true) + * + * @Groups({"entries_for_user", "export_all"}) + */ + private $uuid; + /** * @var string * @@ -88,20 +97,20 @@ class Entry private $content; /** - * @var date + * @var \DateTime * * @ORM\Column(name="created_at", type="datetime") * - * @Groups({"export_all"}) + * @Groups({"entries_for_user", "export_all"}) */ private $createdAt; /** - * @var date + * @var \DateTime * * @ORM\Column(name="updated_at", type="datetime") * - * @Groups({"export_all"}) + * @Groups({"entries_for_user", "export_all"}) */ private $updatedAt; @@ -187,8 +196,6 @@ class Entry * @ORM\JoinColumn(name="tag_id", referencedColumnName="id") * } * ) - * - * @Groups({"entries_for_user", "export_all"}) */ private $tags; @@ -401,7 +408,22 @@ class Entry } /** - * @return string + * Set created_at. + * Only used when importing data from an other service. + * + * @param \DateTime $createdAt + * + * @return Entry + */ + public function setCreatedAt(\DateTime $createdAt) + { + $this->createdAt = $createdAt; + + return $this; + } + + /** + * @return \DateTime */ public function getCreatedAt() { @@ -409,7 +431,7 @@ class Entry } /** - * @return string + * @return \DateTime */ public function getUpdatedAt() { @@ -517,6 +539,21 @@ class Entry return $this->tags; } + /** + * @VirtualProperty + * @SerializedName("tags") + * @Groups({"entries_for_user", "export_all"}) + */ + public function getSerializedTags() + { + $data = []; + foreach ($this->tags as $tag) { + $data[] = $tag->getLabel(); + } + + return $data; + } + /** * @param Tag $tag */ @@ -595,4 +632,37 @@ class Entry { return $this->language; } + + /** + * @return string + */ + public function getUuid() + { + return $this->uuid; + } + + /** + * @param string $uuid + * + * @return Entry + */ + public function setUuid($uuid) + { + $this->uuid = $uuid; + + return $this; + } + + public function generateUuid() + { + if (null === $this->uuid) { + // @see http://blog.kevingomez.fr/til/2015/07/26/why-is-uniqid-slow/ for true parameter + $this->uuid = uniqid('', true); + } + } + + public function cleanUuid() + { + $this->uuid = null; + } } diff --git a/src/Wallabag/CoreBundle/Form/Type/ConfigType.php b/src/Wallabag/CoreBundle/Form/Type/ConfigType.php index 7d25cc805..0bac28740 100644 --- a/src/Wallabag/CoreBundle/Form/Type/ConfigType.php +++ b/src/Wallabag/CoreBundle/Form/Type/ConfigType.php @@ -52,6 +52,9 @@ class ConfigType extends AbstractType 'choices' => array_flip($this->languages), 'label' => 'config.form_settings.language_label', ]) + ->add('pocket_consumer_key', null, [ + 'label' => 'config.form_settings.pocket_consumer_key_label', + ]) ->add('save', SubmitType::class, [ 'label' => 'config.form.save', ]) diff --git a/src/Wallabag/CoreBundle/Helper/ContentProxy.php b/src/Wallabag/CoreBundle/Helper/ContentProxy.php index 5dd684f2d..8019df42a 100644 --- a/src/Wallabag/CoreBundle/Helper/ContentProxy.php +++ b/src/Wallabag/CoreBundle/Helper/ContentProxy.php @@ -95,14 +95,24 @@ class ContentProxy * Assign some tags to an entry. * * @param Entry $entry - * @param array|string $tags An array of tag or a string coma separated of tag + * @param array|string $tags An array of tag or a string coma separated of tag + * @param array $entitiesReady Entities from the EntityManager which are persisted but not yet flushed + * It is mostly to fix duplicate tag on import @see http://stackoverflow.com/a/7879164/569101 */ - public function assignTagsToEntry(Entry $entry, $tags) + public function assignTagsToEntry(Entry $entry, $tags, array $entitiesReady = []) { if (!is_array($tags)) { $tags = explode(',', $tags); } + // keeps only Tag entity from the "not yet flushed entities" + $tagsNotYetFlushed = []; + foreach ($entitiesReady as $entity) { + if ($entity instanceof Tag) { + $tagsNotYetFlushed[$entity->getLabel()] = $entity; + } + } + foreach ($tags as $label) { $label = trim($label); @@ -111,11 +121,15 @@ class ContentProxy continue; } - $tagEntity = $this->tagRepository->findOneByLabel($label); + if (isset($tagsNotYetFlushed[$label])) { + $tagEntity = $tagsNotYetFlushed[$label]; + } else { + $tagEntity = $this->tagRepository->findOneByLabel($label); - if (is_null($tagEntity)) { - $tagEntity = new Tag(); - $tagEntity->setLabel($label); + if (is_null($tagEntity)) { + $tagEntity = new Tag(); + $tagEntity->setLabel($label); + } } // only add the tag on the entry if the relation doesn't exist diff --git a/src/Wallabag/CoreBundle/Helper/EntriesExport.php b/src/Wallabag/CoreBundle/Helper/EntriesExport.php index ccf4e4f38..e50c68a67 100644 --- a/src/Wallabag/CoreBundle/Helper/EntriesExport.php +++ b/src/Wallabag/CoreBundle/Helper/EntriesExport.php @@ -21,7 +21,6 @@ class EntriesExport private $entries = []; private $authors = ['wallabag']; private $language = ''; - private $tags = []; private $footerTemplate = '

Produced by wallabag with %EXPORT_METHOD%

Please open an issue if you have trouble with the display of this E-Book on your device.

@@ -53,10 +52,6 @@ class EntriesExport $this->entries = $entries; - foreach ($entries as $entry) { - $this->tags[] = $entry->getTags(); - } - return $this; } @@ -159,8 +154,8 @@ class EntriesExport // set tags as subjects foreach ($this->entries as $entry) { - foreach ($this->tags as $tag) { - $book->setSubject($tag['value']); + foreach ($entry->getTags() as $tag) { + $book->setSubject($tag->getLabel()); } // the reader in Kobo Devices doesn't likes special caracters @@ -265,8 +260,8 @@ class EntriesExport * Adding actual entries */ foreach ($this->entries as $entry) { - foreach ($this->tags as $tag) { - $pdf->SetKeywords($tag['value']); + foreach ($entry->getTags() as $tag) { + $pdf->SetKeywords($tag->getLabel()); } $pdf->AddPage(); @@ -302,7 +297,7 @@ class EntriesExport $enclosure = '"'; $handle = fopen('php://memory', 'rb+'); - fputcsv($handle, ['Title', 'URL', 'Content', 'Tags', 'MIME Type', 'Language'], $delimiter, $enclosure); + fputcsv($handle, ['Title', 'URL', 'Content', 'Tags', 'MIME Type', 'Language', 'Creation date'], $delimiter, $enclosure); foreach ($this->entries as $entry) { fputcsv( @@ -315,6 +310,7 @@ class EntriesExport implode(', ', $entry->getTags()->toArray()), $entry->getMimetype(), $entry->getLanguage(), + $entry->getCreatedAt()->format('d/m/Y h:i:s'), ], $delimiter, $enclosure diff --git a/src/Wallabag/CoreBundle/Helper/PreparePagerForEntries.php b/src/Wallabag/CoreBundle/Helper/PreparePagerForEntries.php new file mode 100644 index 000000000..f9066bee7 --- /dev/null +++ b/src/Wallabag/CoreBundle/Helper/PreparePagerForEntries.php @@ -0,0 +1,34 @@ +user = $token->getToken()->getUser(); + $this->router = $router; + } + + /** + * @param AdapterInterface $adapter + * @param int $page + * + * @return null|Pagerfanta + */ + public function prepare(AdapterInterface $adapter, $page = 1) + { + $entries = new Pagerfanta($adapter); + $entries->setMaxPerPage($this->user->getConfig()->getItemsPerPage()); + + return $entries; + } +} diff --git a/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php b/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php index 239d09ae7..b490e2090 100644 --- a/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php +++ b/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php @@ -55,6 +55,7 @@ class RuleBasedTagger { $rules = $this->getRulesForUser($user); $entries = []; + $tagsCache = []; foreach ($rules as $rule) { $qb = $this->entryRepository->getBuilderForAllByUser($user->getId()); @@ -62,7 +63,12 @@ class RuleBasedTagger foreach ($entries as $entry) { foreach ($rule->getTags() as $label) { - $tag = $this->getTag($label); + // avoid new tag duplicate by manually caching them + if (!isset($tagsCache[$label])) { + $tagsCache[$label] = $this->getTag($label); + } + + $tag = $tagsCache[$label]; $entry->addTag($tag); } diff --git a/src/Wallabag/CoreBundle/ParamConverter/UsernameRssTokenConverter.php b/src/Wallabag/CoreBundle/ParamConverter/UsernameRssTokenConverter.php index 6ea2a4f3a..40b5673dd 100644 --- a/src/Wallabag/CoreBundle/ParamConverter/UsernameRssTokenConverter.php +++ b/src/Wallabag/CoreBundle/ParamConverter/UsernameRssTokenConverter.php @@ -49,7 +49,7 @@ class UsernameRssTokenConverter implements ParamConverterInterface $em = $this->registry->getManagerForClass($configuration->getClass()); // Check, if class name is what we need - if ('Wallabag\UserBundle\Entity\User' !== $em->getClassMetadata($configuration->getClass())->getName()) { + if (null !== $em && 'Wallabag\UserBundle\Entity\User' !== $em->getClassMetadata($configuration->getClass())->getName()) { return false; } @@ -69,9 +69,8 @@ class UsernameRssTokenConverter implements ParamConverterInterface $username = $request->attributes->get('username'); $rssToken = $request->attributes->get('token'); - // Check, if route attributes exists - if (null === $username || null === $rssToken) { - throw new \InvalidArgumentException('Route attribute is missing'); + if (!$request->attributes->has('username') || !$request->attributes->has('token')) { + return false; } // Get actual entity manager for class diff --git a/src/Wallabag/CoreBundle/Repository/EntryRepository.php b/src/Wallabag/CoreBundle/Repository/EntryRepository.php index 4d45e5f51..cd2b47b9f 100644 --- a/src/Wallabag/CoreBundle/Repository/EntryRepository.php +++ b/src/Wallabag/CoreBundle/Repository/EntryRepository.php @@ -3,6 +3,7 @@ namespace Wallabag\CoreBundle\Repository; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\Query; use Pagerfanta\Adapter\DoctrineORMAdapter; use Pagerfanta\Pagerfanta; use Wallabag\CoreBundle\Entity\Tag; @@ -84,6 +85,22 @@ class EntryRepository extends EntityRepository ; } + /** + * Retrieves untagged entries for a user. + * + * @param int $userId + * + * @return QueryBuilder + */ + public function getBuilderForUntaggedByUser($userId) + { + return $this + ->getBuilderByUser($userId) + ->leftJoin('e.tags', 't') + ->groupBy('e.id') + ->having('count(t.id) = 0'); + } + /** * Find Entries. * @@ -92,12 +109,15 @@ class EntryRepository extends EntityRepository * @param bool $isStarred * @param string $sort * @param string $order + * @param int $since + * @param string $tags * * @return array */ - public function findEntries($userId, $isArchived = null, $isStarred = null, $sort = 'created', $order = 'ASC') + public function findEntries($userId, $isArchived = null, $isStarred = null, $sort = 'created', $order = 'ASC', $since = 0, $tags = '') { $qb = $this->createQueryBuilder('e') + ->leftJoin('e.tags', 't') ->where('e.user =:userId')->setParameter('userId', $userId); if (null !== $isArchived) { @@ -108,6 +128,16 @@ class EntryRepository extends EntityRepository $qb->andWhere('e.isStarred =:isStarred')->setParameter('isStarred', (bool) $isStarred); } + if ($since > 0) { + $qb->andWhere('e.updatedAt > :since')->setParameter('since', new \DateTime(date('Y-m-d H:i:s', $since))); + } + + if ('' !== $tags) { + foreach (explode(',', $tags) as $tag) { + $qb->andWhere('t.label = :label')->setParameter('label', $tag); + } + } + if ('created' === $sort) { $qb->orderBy('e.id', $order); } elseif ('updated' === $sort) { @@ -209,6 +239,19 @@ class EntryRepository extends EntityRepository $this->getEntityManager()->flush(); } + /** + * Remove tags from all user entries. + * + * @param int $userId + * @param Array $tags + */ + public function removeTags($userId, $tags) + { + foreach ($tags as $tag) { + $this->removeTag($userId, $tag); + } + } + /** * Find all entries that are attached to a give tag id. * @@ -238,7 +281,7 @@ class EntryRepository extends EntityRepository public function findByUrlAndUserId($url, $userId) { $res = $this->createQueryBuilder('e') - ->where('e.url = :url')->setParameter('url', $url) + ->where('e.url = :url')->setParameter('url', urldecode($url)) ->andWhere('e.user = :user_id')->setParameter('user_id', $userId) ->getQuery() ->getResult(); @@ -266,4 +309,24 @@ class EntryRepository extends EntityRepository return $qb->getQuery()->getSingleScalarResult(); } + + /** + * Count all entries for a tag and a user. + * + * @param int $userId + * @param int $tagId + * + * @return int + */ + public function countAllEntriesByUserIdAndTagId($userId, $tagId) + { + $qb = $this->createQueryBuilder('e') + ->select('count(e.id)') + ->leftJoin('e.tags', 't') + ->where('e.user=:userId')->setParameter('userId', $userId) + ->andWhere('t.id=:tagId')->setParameter('tagId', $tagId) + ; + + return $qb->getQuery()->getSingleScalarResult(); + } } diff --git a/src/Wallabag/CoreBundle/Repository/TagRepository.php b/src/Wallabag/CoreBundle/Repository/TagRepository.php index abf915fe5..e76878d49 100644 --- a/src/Wallabag/CoreBundle/Repository/TagRepository.php +++ b/src/Wallabag/CoreBundle/Repository/TagRepository.php @@ -7,7 +7,33 @@ use Doctrine\ORM\EntityRepository; class TagRepository extends EntityRepository { /** - * Find Tags. + * Count all tags per user. + * + * @param int $userId + * @param int $cacheLifeTime Duration of the cache for this query + * + * @return int + */ + public function countAllTags($userId, $cacheLifeTime = null) + { + $query = $this->createQueryBuilder('t') + ->select('t.slug') + ->leftJoin('t.entries', 'e') + ->where('e.user = :userId')->setParameter('userId', $userId) + ->groupBy('t.slug') + ->getQuery(); + + if (null !== $cacheLifeTime) { + $query->useQueryCache(true); + $query->useResultCache(true); + $query->setResultCacheLifetime($cacheLifeTime); + } + + return count($query->getArrayResult()); + } + + /** + * Find all tags per user. * * @param int $userId * @@ -16,10 +42,14 @@ class TagRepository extends EntityRepository public function findAllTags($userId) { return $this->createQueryBuilder('t') + ->select('t.slug', 't.label', 't.id') ->leftJoin('t.entries', 'e') ->where('e.user = :userId')->setParameter('userId', $userId) + ->groupBy('t.slug') + ->addGroupBy('t.label') + ->addGroupBy('t.id') ->getQuery() - ->getResult(); + ->getArrayResult(); } /** diff --git a/src/Wallabag/CoreBundle/Resources/config/parameters.yml b/src/Wallabag/CoreBundle/Resources/config/parameters.yml index abd9ab689..6068e84cd 100644 --- a/src/Wallabag/CoreBundle/Resources/config/parameters.yml +++ b/src/Wallabag/CoreBundle/Resources/config/parameters.yml @@ -6,7 +6,3 @@ parameters: google_play: https://play.google.com/store/apps/details?id=fr.gaulupeau.apps.InThePoche ios: https://itunes.apple.com/app/wallabag/id828331015?mt=8 windows: https://www.microsoft.com/store/apps/wallabag/9nblggh11646 - socials_url: - twitter: https://twitter.com/wallabagapp - google_plus: https://plus.google.com/+WallabagOrg/posts - facebook: https://facebook.com/Wallabag diff --git a/src/Wallabag/CoreBundle/Resources/config/services.yml b/src/Wallabag/CoreBundle/Resources/config/services.yml index f8835198c..a4b727f42 100644 --- a/src/Wallabag/CoreBundle/Resources/config/services.yml +++ b/src/Wallabag/CoreBundle/Resources/config/services.yml @@ -29,7 +29,7 @@ services: arguments: - "@doctrine" - wallabag_core.table_prefix_subscriber: + wallabag_core.subscriber.table_prefix: class: Wallabag\CoreBundle\Subscriber\TablePrefixSubscriber arguments: - "%database_table_prefix%" @@ -88,17 +88,6 @@ services: arguments: - WallabagCoreBundle:Tag - wallabag_core.registration_confirmed: - class: Wallabag\CoreBundle\EventListener\RegistrationConfirmedListener - arguments: - - "@doctrine.orm.entity_manager" - - "%wallabag_core.theme%" - - "%wallabag_core.items_on_page%" - - "%wallabag_core.rss_limit%" - - "%wallabag_core.language%" - tags: - - { name: kernel.event_subscriber } - wallabag_core.helper.entries_export: class: Wallabag\CoreBundle\Helper\EntriesExport arguments: @@ -119,3 +108,24 @@ services: class: Wallabag\CoreBundle\Helper\Redirect arguments: - "@router" + + wallabag_core.helper.prepare_pager_for_entries: + class: Wallabag\CoreBundle\Helper\PreparePagerForEntries + arguments: + - "@security.token_storage" + - "@router" + + wallabag_core.redis.client: + class: Predis\Client + arguments: + - + scheme: '%redis_scheme%' + host: '%redis_host%' + port: '%redis_port%' + path: '%redis_path%' + + wallabag_core.exception_controller: + class: Wallabag\CoreBundle\Controller\ExceptionController + arguments: + - '@twig' + - '%kernel.debug%' diff --git a/src/Wallabag/CoreBundle/Resources/public/themes/_global/js/annotator.min.js b/src/Wallabag/CoreBundle/Resources/public/themes/_global/js/annotator.min.js deleted file mode 100644 index fdc35c69b..000000000 --- a/src/Wallabag/CoreBundle/Resources/public/themes/_global/js/annotator.min.js +++ /dev/null @@ -1,23 +0,0 @@ -/* -** Annotator v2.0.0-alpha.3-0f076c7 -** https://github.com/okfn/annotator/ -** -** Copyright 2016, the Annotator project contributors. -** Dual licensed under the MIT and GPLv3 licenses. -** https://github.com/okfn/annotator/blob/master/LICENSE -** -** Built at: 2016-02-26 15:02:00Z -*/ -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.annotator=f()}})(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o1){for(var i=1;i=0&&j=0},isEmptyObject:function(obj){var name;for(name in obj){return false}return true},isPlainObject:function(obj){var key;if(!obj||jQuery.type(obj)!=="object"||obj.nodeType||jQuery.isWindow(obj)){return false}try{if(obj.constructor&&!hasOwn.call(obj,"constructor")&&!hasOwn.call(obj.constructor.prototype,"isPrototypeOf")){return false}}catch(e){return false}if(!support.ownFirst){for(key in obj){return hasOwn.call(obj,key)}}for(key in obj){}return key===undefined||hasOwn.call(obj,key)},type:function(obj){if(obj==null){return obj+""}return typeof obj==="object"||typeof obj==="function"?class2type[toString.call(obj)]||"object":typeof obj},globalEval:function(data){if(data&&jQuery.trim(data)){(window.execScript||function(data){window["eval"].call(window,data)})(data)}},camelCase:function(string){return string.replace(rmsPrefix,"ms-").replace(rdashAlpha,fcamelCase)},nodeName:function(elem,name){return elem.nodeName&&elem.nodeName.toLowerCase()===name.toLowerCase()},each:function(obj,callback){var length,i=0;if(isArrayLike(obj)){length=obj.length;for(;i0&&length-1 in obj}var Sizzle=function(window){var i,support,Expr,getText,isXML,tokenize,compile,select,outermostContext,sortInput,hasDuplicate,setDocument,document,docElem,documentIsHTML,rbuggyQSA,rbuggyMatches,matches,contains,expando="sizzle"+1*new Date,preferredDoc=window.document,dirruns=0,done=0,classCache=createCache(),tokenCache=createCache(),compilerCache=createCache(),sortOrder=function(a,b){if(a===b){hasDuplicate=true}return 0},MAX_NEGATIVE=1<<31,hasOwn={}.hasOwnProperty,arr=[],pop=arr.pop,push_native=arr.push,push=arr.push,slice=arr.slice,indexOf=function(list,elem){var i=0,len=list.length;for(;i+~]|"+whitespace+")"+whitespace+"*"),rattributeQuotes=new RegExp("="+whitespace+"*([^\\]'\"]*?)"+whitespace+"*\\]","g"),rpseudo=new RegExp(pseudos),ridentifier=new RegExp("^"+identifier+"$"),matchExpr={ID:new RegExp("^#("+identifier+")"),CLASS:new RegExp("^\\.("+identifier+")"),TAG:new RegExp("^("+identifier+"|[*])"),ATTR:new RegExp("^"+attributes),PSEUDO:new RegExp("^"+pseudos),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+whitespace+"*(even|odd|(([+-]|)(\\d*)n|)"+whitespace+"*(?:([+-]|)"+whitespace+"*(\\d+)|))"+whitespace+"*\\)|)","i"),bool:new RegExp("^(?:"+booleans+")$","i"),needsContext:new RegExp("^"+whitespace+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+whitespace+"*((?:-\\d)?\\d*)"+whitespace+"*\\)|)(?=[^-]|$)","i")},rinputs=/^(?:input|select|textarea|button)$/i,rheader=/^h\d$/i,rnative=/^[^{]+\{\s*\[native \w/,rquickExpr=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,rsibling=/[+~]/,rescape=/'|\\/g,runescape=new RegExp("\\\\([\\da-f]{1,6}"+whitespace+"?|("+whitespace+")|.)","ig"),funescape=function(_,escaped,escapedWhitespace){var high="0x"+escaped-65536;return high!==high||escapedWhitespace?escaped:high<0?String.fromCharCode(high+65536):String.fromCharCode(high>>10|55296,high&1023|56320)},unloadHandler=function(){setDocument()};try{push.apply(arr=slice.call(preferredDoc.childNodes),preferredDoc.childNodes);arr[preferredDoc.childNodes.length].nodeType}catch(e){push={apply:arr.length?function(target,els){push_native.apply(target,slice.call(els))}:function(target,els){var j=target.length,i=0;while(target[j++]=els[i++]){}target.length=j-1}}}function Sizzle(selector,context,results,seed){var m,i,elem,nid,nidselect,match,groups,newSelector,newContext=context&&context.ownerDocument,nodeType=context?context.nodeType:9;results=results||[];if(typeof selector!=="string"||!selector||nodeType!==1&&nodeType!==9&&nodeType!==11){return results}if(!seed){if((context?context.ownerDocument||context:preferredDoc)!==document){setDocument(context)}context=context||document;if(documentIsHTML){if(nodeType!==11&&(match=rquickExpr.exec(selector))){if(m=match[1]){if(nodeType===9){if(elem=context.getElementById(m)){if(elem.id===m){results.push(elem);return results}}else{return results}}else{if(newContext&&(elem=newContext.getElementById(m))&&contains(context,elem)&&elem.id===m){results.push(elem);return results}}}else if(match[2]){push.apply(results,context.getElementsByTagName(selector));return results}else if((m=match[3])&&support.getElementsByClassName&&context.getElementsByClassName){push.apply(results,context.getElementsByClassName(m));return results}}if(support.qsa&&!compilerCache[selector+" "]&&(!rbuggyQSA||!rbuggyQSA.test(selector))){if(nodeType!==1){newContext=context;newSelector=selector}else if(context.nodeName.toLowerCase()!=="object"){if(nid=context.getAttribute("id")){nid=nid.replace(rescape,"\\$&")}else{context.setAttribute("id",nid=expando)}groups=tokenize(selector);i=groups.length;nidselect=ridentifier.test(nid)?"#"+nid:"[id='"+nid+"']";while(i--){groups[i]=nidselect+" "+toSelector(groups[i])}newSelector=groups.join(",");newContext=rsibling.test(selector)&&testContext(context.parentNode)||context}if(newSelector){try{push.apply(results,newContext.querySelectorAll(newSelector));return results}catch(qsaError){}finally{if(nid===expando){context.removeAttribute("id")}}}}}}return select(selector.replace(rtrim,"$1"),context,results,seed)}function createCache(){var keys=[];function cache(key,value){if(keys.push(key+" ")>Expr.cacheLength){delete cache[keys.shift()]}return cache[key+" "]=value}return cache}function markFunction(fn){fn[expando]=true; -return fn}function assert(fn){var div=document.createElement("div");try{return!!fn(div)}catch(e){return false}finally{if(div.parentNode){div.parentNode.removeChild(div)}div=null}}function addHandle(attrs,handler){var arr=attrs.split("|"),i=arr.length;while(i--){Expr.attrHandle[arr[i]]=handler}}function siblingCheck(a,b){var cur=b&&a,diff=cur&&a.nodeType===1&&b.nodeType===1&&(~b.sourceIndex||MAX_NEGATIVE)-(~a.sourceIndex||MAX_NEGATIVE);if(diff){return diff}if(cur){while(cur=cur.nextSibling){if(cur===b){return-1}}}return a?1:-1}function createInputPseudo(type){return function(elem){var name=elem.nodeName.toLowerCase();return name==="input"&&elem.type===type}}function createButtonPseudo(type){return function(elem){var name=elem.nodeName.toLowerCase();return(name==="input"||name==="button")&&elem.type===type}}function createPositionalPseudo(fn){return markFunction(function(argument){argument=+argument;return markFunction(function(seed,matches){var j,matchIndexes=fn([],seed.length,argument),i=matchIndexes.length;while(i--){if(seed[j=matchIndexes[i]]){seed[j]=!(matches[j]=seed[j])}}})})}function testContext(context){return context&&typeof context.getElementsByTagName!=="undefined"&&context}support=Sizzle.support={};isXML=Sizzle.isXML=function(elem){var documentElement=elem&&(elem.ownerDocument||elem).documentElement;return documentElement?documentElement.nodeName!=="HTML":false};setDocument=Sizzle.setDocument=function(node){var hasCompare,parent,doc=node?node.ownerDocument||node:preferredDoc;if(doc===document||doc.nodeType!==9||!doc.documentElement){return document}document=doc;docElem=document.documentElement;documentIsHTML=!isXML(document);if((parent=document.defaultView)&&parent.top!==parent){if(parent.addEventListener){parent.addEventListener("unload",unloadHandler,false)}else if(parent.attachEvent){parent.attachEvent("onunload",unloadHandler)}}support.attributes=assert(function(div){div.className="i";return!div.getAttribute("className")});support.getElementsByTagName=assert(function(div){div.appendChild(document.createComment(""));return!div.getElementsByTagName("*").length});support.getElementsByClassName=rnative.test(document.getElementsByClassName);support.getById=assert(function(div){docElem.appendChild(div).id=expando;return!document.getElementsByName||!document.getElementsByName(expando).length});if(support.getById){Expr.find["ID"]=function(id,context){if(typeof context.getElementById!=="undefined"&&documentIsHTML){var m=context.getElementById(id);return m?[m]:[]}};Expr.filter["ID"]=function(id){var attrId=id.replace(runescape,funescape);return function(elem){return elem.getAttribute("id")===attrId}}}else{delete Expr.find["ID"];Expr.filter["ID"]=function(id){var attrId=id.replace(runescape,funescape);return function(elem){var node=typeof elem.getAttributeNode!=="undefined"&&elem.getAttributeNode("id");return node&&node.value===attrId}}}Expr.find["TAG"]=support.getElementsByTagName?function(tag,context){if(typeof context.getElementsByTagName!=="undefined"){return context.getElementsByTagName(tag)}else if(support.qsa){return context.querySelectorAll(tag)}}:function(tag,context){var elem,tmp=[],i=0,results=context.getElementsByTagName(tag);if(tag==="*"){while(elem=results[i++]){if(elem.nodeType===1){tmp.push(elem)}}return tmp}return results};Expr.find["CLASS"]=support.getElementsByClassName&&function(className,context){if(typeof context.getElementsByClassName!=="undefined"&&documentIsHTML){return context.getElementsByClassName(className)}};rbuggyMatches=[];rbuggyQSA=[];if(support.qsa=rnative.test(document.querySelectorAll)){assert(function(div){docElem.appendChild(div).innerHTML=""+"";if(div.querySelectorAll("[msallowcapture^='']").length){rbuggyQSA.push("[*^$]="+whitespace+"*(?:''|\"\")")}if(!div.querySelectorAll("[selected]").length){rbuggyQSA.push("\\["+whitespace+"*(?:value|"+booleans+")")}if(!div.querySelectorAll("[id~="+expando+"-]").length){rbuggyQSA.push("~=")}if(!div.querySelectorAll(":checked").length){rbuggyQSA.push(":checked")}if(!div.querySelectorAll("a#"+expando+"+*").length){rbuggyQSA.push(".#.+[+~]")}});assert(function(div){var input=document.createElement("input");input.setAttribute("type","hidden");div.appendChild(input).setAttribute("name","D");if(div.querySelectorAll("[name=d]").length){rbuggyQSA.push("name"+whitespace+"*[*^$|!~]?=")}if(!div.querySelectorAll(":enabled").length){rbuggyQSA.push(":enabled",":disabled")}div.querySelectorAll("*,:x");rbuggyQSA.push(",.*:")})}if(support.matchesSelector=rnative.test(matches=docElem.matches||docElem.webkitMatchesSelector||docElem.mozMatchesSelector||docElem.oMatchesSelector||docElem.msMatchesSelector)){assert(function(div){support.disconnectedMatch=matches.call(div,"div");matches.call(div,"[s!='']:x");rbuggyMatches.push("!=",pseudos)})}rbuggyQSA=rbuggyQSA.length&&new RegExp(rbuggyQSA.join("|"));rbuggyMatches=rbuggyMatches.length&&new RegExp(rbuggyMatches.join("|"));hasCompare=rnative.test(docElem.compareDocumentPosition);contains=hasCompare||rnative.test(docElem.contains)?function(a,b){var adown=a.nodeType===9?a.documentElement:a,bup=b&&b.parentNode;return a===bup||!!(bup&&bup.nodeType===1&&(adown.contains?adown.contains(bup):a.compareDocumentPosition&&a.compareDocumentPosition(bup)&16))}:function(a,b){if(b){while(b=b.parentNode){if(b===a){return true}}}return false};sortOrder=hasCompare?function(a,b){if(a===b){hasDuplicate=true;return 0}var compare=!a.compareDocumentPosition-!b.compareDocumentPosition;if(compare){return compare}compare=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1;if(compare&1||!support.sortDetached&&b.compareDocumentPosition(a)===compare){if(a===document||a.ownerDocument===preferredDoc&&contains(preferredDoc,a)){return-1}if(b===document||b.ownerDocument===preferredDoc&&contains(preferredDoc,b)){return 1}return sortInput?indexOf(sortInput,a)-indexOf(sortInput,b):0}return compare&4?-1:1}:function(a,b){if(a===b){hasDuplicate=true;return 0}var cur,i=0,aup=a.parentNode,bup=b.parentNode,ap=[a],bp=[b];if(!aup||!bup){return a===document?-1:b===document?1:aup?-1:bup?1:sortInput?indexOf(sortInput,a)-indexOf(sortInput,b):0}else if(aup===bup){return siblingCheck(a,b)}cur=a;while(cur=cur.parentNode){ap.unshift(cur)}cur=b;while(cur=cur.parentNode){bp.unshift(cur)}while(ap[i]===bp[i]){i++}return i?siblingCheck(ap[i],bp[i]):ap[i]===preferredDoc?-1:bp[i]===preferredDoc?1:0};return document};Sizzle.matches=function(expr,elements){return Sizzle(expr,null,null,elements)};Sizzle.matchesSelector=function(elem,expr){if((elem.ownerDocument||elem)!==document){setDocument(elem)}expr=expr.replace(rattributeQuotes,"='$1']");if(support.matchesSelector&&documentIsHTML&&!compilerCache[expr+" "]&&(!rbuggyMatches||!rbuggyMatches.test(expr))&&(!rbuggyQSA||!rbuggyQSA.test(expr))){try{var ret=matches.call(elem,expr);if(ret||support.disconnectedMatch||elem.document&&elem.document.nodeType!==11){return ret}}catch(e){}}return Sizzle(expr,document,null,[elem]).length>0};Sizzle.contains=function(context,elem){if((context.ownerDocument||context)!==document){setDocument(context)}return contains(context,elem)};Sizzle.attr=function(elem,name){if((elem.ownerDocument||elem)!==document){setDocument(elem)}var fn=Expr.attrHandle[name.toLowerCase()],val=fn&&hasOwn.call(Expr.attrHandle,name.toLowerCase())?fn(elem,name,!documentIsHTML):undefined;return val!==undefined?val:support.attributes||!documentIsHTML?elem.getAttribute(name):(val=elem.getAttributeNode(name))&&val.specified?val.value:null};Sizzle.error=function(msg){throw new Error("Syntax error, unrecognized expression: "+msg)};Sizzle.uniqueSort=function(results){var elem,duplicates=[],j=0,i=0;hasDuplicate=!support.detectDuplicates;sortInput=!support.sortStable&&results.slice(0);results.sort(sortOrder);if(hasDuplicate){while(elem=results[i++]){if(elem===results[i]){j=duplicates.push(i)}}while(j--){results.splice(duplicates[j],1)}}sortInput=null;return results};getText=Sizzle.getText=function(elem){var node,ret="",i=0,nodeType=elem.nodeType;if(!nodeType){while(node=elem[i++]){ret+=getText(node)}}else if(nodeType===1||nodeType===9||nodeType===11){if(typeof elem.textContent==="string"){return elem.textContent}else{for(elem=elem.firstChild;elem;elem=elem.nextSibling){ret+=getText(elem)}}}else if(nodeType===3||nodeType===4){return elem.nodeValue}return ret};Expr=Sizzle.selectors={cacheLength:50,createPseudo:markFunction,match:matchExpr,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:true}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:true},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(match){match[1]=match[1].replace(runescape,funescape);match[3]=(match[3]||match[4]||match[5]||"").replace(runescape,funescape);if(match[2]==="~="){match[3]=" "+match[3]+" "}return match.slice(0,4)},CHILD:function(match){match[1]=match[1].toLowerCase();if(match[1].slice(0,3)==="nth"){if(!match[3]){Sizzle.error(match[0])}match[4]=+(match[4]?match[5]+(match[6]||1):2*(match[3]==="even"||match[3]==="odd"));match[5]=+(match[7]+match[8]||match[3]==="odd")}else if(match[3]){Sizzle.error(match[0])}return match},PSEUDO:function(match){var excess,unquoted=!match[6]&&match[2];if(matchExpr["CHILD"].test(match[0])){return null}if(match[3]){match[2]=match[4]||match[5]||""}else if(unquoted&&rpseudo.test(unquoted)&&(excess=tokenize(unquoted,true))&&(excess=unquoted.indexOf(")",unquoted.length-excess)-unquoted.length)){match[0]=match[0].slice(0,excess);match[2]=unquoted.slice(0,excess)}return match.slice(0,3)}},filter:{TAG:function(nodeNameSelector){var nodeName=nodeNameSelector.replace(runescape,funescape).toLowerCase();return nodeNameSelector==="*"?function(){return true}:function(elem){return elem.nodeName&&elem.nodeName.toLowerCase()===nodeName}},CLASS:function(className){var pattern=classCache[className+" "];return pattern||(pattern=new RegExp("(^|"+whitespace+")"+className+"("+whitespace+"|$)"))&&classCache(className,function(elem){return pattern.test(typeof elem.className==="string"&&elem.className||typeof elem.getAttribute!=="undefined"&&elem.getAttribute("class")||"")})},ATTR:function(name,operator,check){return function(elem){var result=Sizzle.attr(elem,name);if(result==null){return operator==="!="}if(!operator){return true}result+="";return operator==="="?result===check:operator==="!="?result!==check:operator==="^="?check&&result.indexOf(check)===0:operator==="*="?check&&result.indexOf(check)>-1:operator==="$="?check&&result.slice(-check.length)===check:operator==="~="?(" "+result.replace(rwhitespace," ")+" ").indexOf(check)>-1:operator==="|="?result===check||result.slice(0,check.length+1)===check+"-":false}},CHILD:function(type,what,argument,first,last){var simple=type.slice(0,3)!=="nth",forward=type.slice(-4)!=="last",ofType=what==="of-type";return first===1&&last===0?function(elem){return!!elem.parentNode}:function(elem,context,xml){var cache,uniqueCache,outerCache,node,nodeIndex,start,dir=simple!==forward?"nextSibling":"previousSibling",parent=elem.parentNode,name=ofType&&elem.nodeName.toLowerCase(),useCache=!xml&&!ofType,diff=false;if(parent){if(simple){while(dir){node=elem;while(node=node[dir]){if(ofType?node.nodeName.toLowerCase()===name:node.nodeType===1){return false}}start=dir=type==="only"&&!start&&"nextSibling"}return true}start=[forward?parent.firstChild:parent.lastChild];if(forward&&useCache){node=parent;outerCache=node[expando]||(node[expando]={});uniqueCache=outerCache[node.uniqueID]||(outerCache[node.uniqueID]={});cache=uniqueCache[type]||[];nodeIndex=cache[0]===dirruns&&cache[1];diff=nodeIndex&&cache[2];node=nodeIndex&&parent.childNodes[nodeIndex];while(node=++nodeIndex&&node&&node[dir]||(diff=nodeIndex=0)||start.pop()){if(node.nodeType===1&&++diff&&node===elem){uniqueCache[type]=[dirruns,nodeIndex,diff];break}}}else{if(useCache){node=elem;outerCache=node[expando]||(node[expando]={});uniqueCache=outerCache[node.uniqueID]||(outerCache[node.uniqueID]={});cache=uniqueCache[type]||[];nodeIndex=cache[0]===dirruns&&cache[1];diff=nodeIndex}if(diff===false){while(node=++nodeIndex&&node&&node[dir]||(diff=nodeIndex=0)||start.pop()){if((ofType?node.nodeName.toLowerCase()===name:node.nodeType===1)&&++diff){if(useCache){outerCache=node[expando]||(node[expando]={});uniqueCache=outerCache[node.uniqueID]||(outerCache[node.uniqueID]={});uniqueCache[type]=[dirruns,diff]}if(node===elem){break}}}}}diff-=last;return diff===first||diff%first===0&&diff/first>=0}}},PSEUDO:function(pseudo,argument){var args,fn=Expr.pseudos[pseudo]||Expr.setFilters[pseudo.toLowerCase()]||Sizzle.error("unsupported pseudo: "+pseudo);if(fn[expando]){return fn(argument)}if(fn.length>1){args=[pseudo,pseudo,"",argument];return Expr.setFilters.hasOwnProperty(pseudo.toLowerCase())?markFunction(function(seed,matches){var idx,matched=fn(seed,argument),i=matched.length;while(i--){idx=indexOf(seed,matched[i]);seed[idx]=!(matches[idx]=matched[i])}}):function(elem){return fn(elem,0,args)}}return fn}},pseudos:{not:markFunction(function(selector){var input=[],results=[],matcher=compile(selector.replace(rtrim,"$1"));return matcher[expando]?markFunction(function(seed,matches,context,xml){var elem,unmatched=matcher(seed,null,xml,[]),i=seed.length;while(i--){if(elem=unmatched[i]){seed[i]=!(matches[i]=elem)}}}):function(elem,context,xml){input[0]=elem;matcher(input,null,xml,results);input[0]=null;return!results.pop()}}),has:markFunction(function(selector){return function(elem){return Sizzle(selector,elem).length>0}}),contains:markFunction(function(text){text=text.replace(runescape,funescape);return function(elem){return(elem.textContent||elem.innerText||getText(elem)).indexOf(text)>-1}}),lang:markFunction(function(lang){if(!ridentifier.test(lang||"")){Sizzle.error("unsupported lang: "+lang)}lang=lang.replace(runescape,funescape).toLowerCase();return function(elem){var elemLang;do{if(elemLang=documentIsHTML?elem.lang:elem.getAttribute("xml:lang")||elem.getAttribute("lang")){elemLang=elemLang.toLowerCase();return elemLang===lang||elemLang.indexOf(lang+"-")===0}}while((elem=elem.parentNode)&&elem.nodeType===1);return false}}),target:function(elem){var hash=window.location&&window.location.hash;return hash&&hash.slice(1)===elem.id},root:function(elem){return elem===docElem},focus:function(elem){return elem===document.activeElement&&(!document.hasFocus||document.hasFocus())&&!!(elem.type||elem.href||~elem.tabIndex)},enabled:function(elem){return elem.disabled===false},disabled:function(elem){return elem.disabled===true},checked:function(elem){var nodeName=elem.nodeName.toLowerCase();return nodeName==="input"&&!!elem.checked||nodeName==="option"&&!!elem.selected},selected:function(elem){if(elem.parentNode){elem.parentNode.selectedIndex}return elem.selected===true},empty:function(elem){for(elem=elem.firstChild;elem;elem=elem.nextSibling){if(elem.nodeType<6){return false}}return true},parent:function(elem){return!Expr.pseudos["empty"](elem)},header:function(elem){return rheader.test(elem.nodeName)},input:function(elem){return rinputs.test(elem.nodeName)},button:function(elem){var name=elem.nodeName.toLowerCase();return name==="input"&&elem.type==="button"||name==="button"},text:function(elem){var attr;return elem.nodeName.toLowerCase()==="input"&&elem.type==="text"&&((attr=elem.getAttribute("type"))==null||attr.toLowerCase()==="text")},first:createPositionalPseudo(function(){return[0]}),last:createPositionalPseudo(function(matchIndexes,length){return[length-1]}),eq:createPositionalPseudo(function(matchIndexes,length,argument){return[argument<0?argument+length:argument]}),even:createPositionalPseudo(function(matchIndexes,length){var i=0;for(;i=0;){matchIndexes.push(i)}return matchIndexes}),gt:createPositionalPseudo(function(matchIndexes,length,argument){var i=argument<0?argument+length:argument;for(;++i1?function(elem,context,xml){var i=matchers.length;while(i--){if(!matchers[i](elem,context,xml)){return false}}return true}:matchers[0]}function multipleContexts(selector,contexts,results){var i=0,len=contexts.length;for(;i-1){seed[temp]=!(results[temp]=elem)}}}}else{matcherOut=condense(matcherOut===results?matcherOut.splice(preexisting,matcherOut.length):matcherOut);if(postFinder){postFinder(null,results,matcherOut,xml)}else{push.apply(results,matcherOut)}}})}function matcherFromTokens(tokens){var checkContext,matcher,j,len=tokens.length,leadingRelative=Expr.relative[tokens[0].type],implicitRelative=leadingRelative||Expr.relative[" "],i=leadingRelative?1:0,matchContext=addCombinator(function(elem){return elem===checkContext},implicitRelative,true),matchAnyContext=addCombinator(function(elem){return indexOf(checkContext,elem)>-1},implicitRelative,true),matchers=[function(elem,context,xml){var ret=!leadingRelative&&(xml||context!==outermostContext)||((checkContext=context).nodeType?matchContext(elem,context,xml):matchAnyContext(elem,context,xml));checkContext=null;return ret}];for(;i1&&elementMatcher(matchers),i>1&&toSelector(tokens.slice(0,i-1).concat({value:tokens[i-2].type===" "?"*":""})).replace(rtrim,"$1"),matcher,i0,byElement=elementMatchers.length>0,superMatcher=function(seed,context,xml,results,outermost){var elem,j,matcher,matchedCount=0,i="0",unmatched=seed&&[],setMatched=[],contextBackup=outermostContext,elems=seed||byElement&&Expr.find["TAG"]("*",outermost),dirrunsUnique=dirruns+=contextBackup==null?1:Math.random()||.1,len=elems.length;if(outermost){outermostContext=context===document||context||outermost}for(;i!==len&&(elem=elems[i])!=null;i++){if(byElement&&elem){j=0;if(!context&&elem.ownerDocument!==document){setDocument(elem);xml=!documentIsHTML}while(matcher=elementMatchers[j++]){if(matcher(elem,context||document,xml)){results.push(elem);break}}if(outermost){dirruns=dirrunsUnique}}if(bySet){if(elem=!matcher&&elem){matchedCount--}if(seed){unmatched.push(elem)}}}matchedCount+=i;if(bySet&&i!==matchedCount){j=0;while(matcher=setMatchers[j++]){matcher(unmatched,setMatched,context,xml)}if(seed){if(matchedCount>0){while(i--){if(!(unmatched[i]||setMatched[i])){setMatched[i]=pop.call(results)}}}setMatched=condense(setMatched)}push.apply(results,setMatched);if(outermost&&!seed&&setMatched.length>0&&matchedCount+setMatchers.length>1){Sizzle.uniqueSort(results)}}if(outermost){dirruns=dirrunsUnique;outermostContext=contextBackup}return unmatched};return bySet?markFunction(superMatcher):superMatcher}compile=Sizzle.compile=function(selector,match){var i,setMatchers=[],elementMatchers=[],cached=compilerCache[selector+" "];if(!cached){if(!match){match=tokenize(selector)}i=match.length;while(i--){cached=matcherFromTokens(match[i]);if(cached[expando]){setMatchers.push(cached)}else{elementMatchers.push(cached)}}cached=compilerCache(selector,matcherFromGroupMatchers(elementMatchers,setMatchers));cached.selector=selector}return cached};select=Sizzle.select=function(selector,context,results,seed){var i,tokens,token,type,find,compiled=typeof selector==="function"&&selector,match=!seed&&tokenize(selector=compiled.selector||selector);results=results||[];if(match.length===1){tokens=match[0]=match[0].slice(0);if(tokens.length>2&&(token=tokens[0]).type==="ID"&&support.getById&&context.nodeType===9&&documentIsHTML&&Expr.relative[tokens[1].type]){context=(Expr.find["ID"](token.matches[0].replace(runescape,funescape),context)||[])[0];if(!context){return results}else if(compiled){context=context.parentNode}selector=selector.slice(tokens.shift().value.length)}i=matchExpr["needsContext"].test(selector)?0:tokens.length;while(i--){token=tokens[i];if(Expr.relative[type=token.type]){break}if(find=Expr.find[type]){if(seed=find(token.matches[0].replace(runescape,funescape),rsibling.test(tokens[0].type)&&testContext(context.parentNode)||context)){tokens.splice(i,1);selector=seed.length&&toSelector(tokens);if(!selector){push.apply(results,seed);return results}break}}}}(compiled||compile(selector,match))(seed,context,!documentIsHTML,results,!context||rsibling.test(selector)&&testContext(context.parentNode)||context);return results};support.sortStable=expando.split("").sort(sortOrder).join("")===expando;support.detectDuplicates=!!hasDuplicate;setDocument();support.sortDetached=assert(function(div1){return div1.compareDocumentPosition(document.createElement("div"))&1});if(!assert(function(div){div.innerHTML="";return div.firstChild.getAttribute("href")==="#"})){addHandle("type|href|height|width",function(elem,name,isXML){if(!isXML){return elem.getAttribute(name,name.toLowerCase()==="type"?1:2)}})}if(!support.attributes||!assert(function(div){div.innerHTML="";div.firstChild.setAttribute("value","");return div.firstChild.getAttribute("value")===""})){addHandle("value",function(elem,name,isXML){if(!isXML&&elem.nodeName.toLowerCase()==="input"){return elem.defaultValue}})}if(!assert(function(div){return div.getAttribute("disabled")==null})){addHandle(booleans,function(elem,name,isXML){var val;if(!isXML){return elem[name]===true?name.toLowerCase():(val=elem.getAttributeNode(name))&&val.specified?val.value:null}})}return Sizzle}(window);jQuery.find=Sizzle;jQuery.expr=Sizzle.selectors;jQuery.expr[":"]=jQuery.expr.pseudos;jQuery.uniqueSort=jQuery.unique=Sizzle.uniqueSort;jQuery.text=Sizzle.getText;jQuery.isXMLDoc=Sizzle.isXML;jQuery.contains=Sizzle.contains;var dir=function(elem,dir,until){var matched=[],truncate=until!==undefined;while((elem=elem[dir])&&elem.nodeType!==9){if(elem.nodeType===1){if(truncate&&jQuery(elem).is(until)){break}matched.push(elem)}}return matched};var siblings=function(n,elem){var matched=[];for(;n;n=n.nextSibling){if(n.nodeType===1&&n!==elem){matched.push(n)}}return matched};var rneedsContext=jQuery.expr.match.needsContext;var rsingleTag=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;var risSimple=/^.[^:#\[\.,]*$/;function winnow(elements,qualifier,not){if(jQuery.isFunction(qualifier)){return jQuery.grep(elements,function(elem,i){return!!qualifier.call(elem,i,elem)!==not})}if(qualifier.nodeType){return jQuery.grep(elements,function(elem){return elem===qualifier!==not})}if(typeof qualifier==="string"){if(risSimple.test(qualifier)){return jQuery.filter(qualifier,elements,not)}qualifier=jQuery.filter(qualifier,elements)}return jQuery.grep(elements,function(elem){return jQuery.inArray(elem,qualifier)>-1!==not})}jQuery.filter=function(expr,elems,not){var elem=elems[0];if(not){expr=":not("+expr+")"}return elems.length===1&&elem.nodeType===1?jQuery.find.matchesSelector(elem,expr)?[elem]:[]:jQuery.find.matches(expr,jQuery.grep(elems,function(elem){return elem.nodeType===1}))};jQuery.fn.extend({find:function(selector){var i,ret=[],self=this,len=self.length;if(typeof selector!=="string"){return this.pushStack(jQuery(selector).filter(function(){for(i=0;i1?jQuery.unique(ret):ret);ret.selector=this.selector?this.selector+" "+selector:selector;return ret},filter:function(selector){return this.pushStack(winnow(this,selector||[],false))},not:function(selector){return this.pushStack(winnow(this,selector||[],true))},is:function(selector){return!!winnow(this,typeof selector==="string"&&rneedsContext.test(selector)?jQuery(selector):selector||[],false).length}});var rootjQuery,rquickExpr=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,init=jQuery.fn.init=function(selector,context,root){var match,elem;if(!selector){return this}root=root||rootjQuery;if(typeof selector==="string"){if(selector.charAt(0)==="<"&&selector.charAt(selector.length-1)===">"&&selector.length>=3){match=[null,selector,null]}else{match=rquickExpr.exec(selector)}if(match&&(match[1]||!context)){if(match[1]){context=context instanceof jQuery?context[0]:context;jQuery.merge(this,jQuery.parseHTML(match[1],context&&context.nodeType?context.ownerDocument||context:document,true));if(rsingleTag.test(match[1])&&jQuery.isPlainObject(context)){for(match in context){if(jQuery.isFunction(this[match])){this[match](context[match])}else{this.attr(match,context[match])}}}return this}else{elem=document.getElementById(match[2]);if(elem&&elem.parentNode){if(elem.id!==match[2]){return rootjQuery.find(selector)}this.length=1;this[0]=elem}this.context=document;this.selector=selector;return this}}else if(!context||context.jquery){return(context||root).find(selector)}else{return this.constructor(context).find(selector)}}else if(selector.nodeType){this.context=this[0]=selector;this.length=1;return this}else if(jQuery.isFunction(selector)){return typeof root.ready!=="undefined"?root.ready(selector):selector(jQuery)}if(selector.selector!==undefined){this.selector=selector.selector;this.context=selector.context}return jQuery.makeArray(selector,this)};init.prototype=jQuery.fn;rootjQuery=jQuery(document);var rparentsprev=/^(?:parents|prev(?:Until|All))/,guaranteedUnique={children:true,contents:true,next:true,prev:true};jQuery.fn.extend({has:function(target){var i,targets=jQuery(target,this),len=targets.length;return this.filter(function(){for(i=0;i-1:cur.nodeType===1&&jQuery.find.matchesSelector(cur,selectors))){matched.push(cur);break}}}return this.pushStack(matched.length>1?jQuery.uniqueSort(matched):matched)},index:function(elem){if(!elem){return this[0]&&this[0].parentNode?this.first().prevAll().length:-1}if(typeof elem==="string"){return jQuery.inArray(this[0],jQuery(elem))}return jQuery.inArray(elem.jquery?elem[0]:elem,this)},add:function(selector,context){return this.pushStack(jQuery.uniqueSort(jQuery.merge(this.get(),jQuery(selector,context))))},addBack:function(selector){return this.add(selector==null?this.prevObject:this.prevObject.filter(selector))}});function sibling(cur,dir){do{cur=cur[dir]}while(cur&&cur.nodeType!==1);return cur}jQuery.each({parent:function(elem){var parent=elem.parentNode;return parent&&parent.nodeType!==11?parent:null},parents:function(elem){return dir(elem,"parentNode")},parentsUntil:function(elem,i,until){return dir(elem,"parentNode",until)},next:function(elem){return sibling(elem,"nextSibling")},prev:function(elem){return sibling(elem,"previousSibling")},nextAll:function(elem){return dir(elem,"nextSibling")},prevAll:function(elem){return dir(elem,"previousSibling")},nextUntil:function(elem,i,until){return dir(elem,"nextSibling",until)},prevUntil:function(elem,i,until){return dir(elem,"previousSibling",until)},siblings:function(elem){return siblings((elem.parentNode||{}).firstChild,elem)},children:function(elem){return siblings(elem.firstChild)},contents:function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.merge([],elem.childNodes)}},function(name,fn){jQuery.fn[name]=function(until,selector){var ret=jQuery.map(this,fn,until);if(name.slice(-5)!=="Until"){selector=until}if(selector&&typeof selector==="string"){ret=jQuery.filter(selector,ret)}if(this.length>1){if(!guaranteedUnique[name]){ret=jQuery.uniqueSort(ret)}if(rparentsprev.test(name)){ret=ret.reverse()}}return this.pushStack(ret)}});var rnotwhite=/\S+/g;function createOptions(options){ -var object={};jQuery.each(options.match(rnotwhite)||[],function(_,flag){object[flag]=true});return object}jQuery.Callbacks=function(options){options=typeof options==="string"?createOptions(options):jQuery.extend({},options);var firing,memory,fired,locked,list=[],queue=[],firingIndex=-1,fire=function(){locked=options.once;fired=firing=true;for(;queue.length;firingIndex=-1){memory=queue.shift();while(++firingIndex-1){list.splice(index,1);if(index<=firingIndex){firingIndex--}}});return this},has:function(fn){return fn?jQuery.inArray(fn,list)>-1:list.length>0},empty:function(){if(list){list=[]}return this},disable:function(){locked=queue=[];list=memory="";return this},disabled:function(){return!list},lock:function(){locked=true;if(!memory){self.disable()}return this},locked:function(){return!!locked},fireWith:function(context,args){if(!locked){args=args||[];args=[context,args.slice?args.slice():args];queue.push(args);if(!firing){fire()}}return this},fire:function(){self.fireWith(this,arguments);return this},fired:function(){return!!fired}};return self};jQuery.extend({Deferred:function(func){var tuples=[["resolve","done",jQuery.Callbacks("once memory"),"resolved"],["reject","fail",jQuery.Callbacks("once memory"),"rejected"],["notify","progress",jQuery.Callbacks("memory")]],state="pending",promise={state:function(){return state},always:function(){deferred.done(arguments).fail(arguments);return this},then:function(){var fns=arguments;return jQuery.Deferred(function(newDefer){jQuery.each(tuples,function(i,tuple){var fn=jQuery.isFunction(fns[i])&&fns[i];deferred[tuple[1]](function(){var returned=fn&&fn.apply(this,arguments);if(returned&&jQuery.isFunction(returned.promise)){returned.promise().progress(newDefer.notify).done(newDefer.resolve).fail(newDefer.reject)}else{newDefer[tuple[0]+"With"](this===promise?newDefer.promise():this,fn?[returned]:arguments)}})});fns=null}).promise()},promise:function(obj){return obj!=null?jQuery.extend(obj,promise):promise}},deferred={};promise.pipe=promise.then;jQuery.each(tuples,function(i,tuple){var list=tuple[2],stateString=tuple[3];promise[tuple[1]]=list.add;if(stateString){list.add(function(){state=stateString},tuples[i^1][2].disable,tuples[2][2].lock)}deferred[tuple[0]]=function(){deferred[tuple[0]+"With"](this===deferred?promise:this,arguments);return this};deferred[tuple[0]+"With"]=list.fireWith});promise.promise(deferred);if(func){func.call(deferred,deferred)}return deferred},when:function(subordinate){var i=0,resolveValues=slice.call(arguments),length=resolveValues.length,remaining=length!==1||subordinate&&jQuery.isFunction(subordinate.promise)?length:0,deferred=remaining===1?subordinate:jQuery.Deferred(),updateFunc=function(i,contexts,values){return function(value){contexts[i]=this;values[i]=arguments.length>1?slice.call(arguments):value;if(values===progressValues){deferred.notifyWith(contexts,values)}else if(!--remaining){deferred.resolveWith(contexts,values)}}},progressValues,progressContexts,resolveContexts;if(length>1){progressValues=new Array(length);progressContexts=new Array(length);resolveContexts=new Array(length);for(;i0){return}readyList.resolveWith(document,[jQuery]);if(jQuery.fn.triggerHandler){jQuery(document).triggerHandler("ready");jQuery(document).off("ready")}}});function detach(){if(document.addEventListener){document.removeEventListener("DOMContentLoaded",completed);window.removeEventListener("load",completed)}else{document.detachEvent("onreadystatechange",completed);window.detachEvent("onload",completed)}}function completed(){if(document.addEventListener||window.event.type==="load"||document.readyState==="complete"){detach();jQuery.ready()}}jQuery.ready.promise=function(obj){if(!readyList){readyList=jQuery.Deferred();if(document.readyState==="complete"||document.readyState!=="loading"&&!document.documentElement.doScroll){window.setTimeout(jQuery.ready)}else if(document.addEventListener){document.addEventListener("DOMContentLoaded",completed);window.addEventListener("load",completed)}else{document.attachEvent("onreadystatechange",completed);window.attachEvent("onload",completed);var top=false;try{top=window.frameElement==null&&document.documentElement}catch(e){}if(top&&top.doScroll){(function doScrollCheck(){if(!jQuery.isReady){try{top.doScroll("left")}catch(e){return window.setTimeout(doScrollCheck,50)}detach();jQuery.ready()}})()}}}return readyList.promise(obj)};jQuery.ready.promise();var i;for(i in jQuery(support)){break}support.ownFirst=i==="0";support.inlineBlockNeedsLayout=false;jQuery(function(){var val,div,body,container;body=document.getElementsByTagName("body")[0];if(!body||!body.style){return}div=document.createElement("div");container=document.createElement("div");container.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px";body.appendChild(container).appendChild(div);if(typeof div.style.zoom!=="undefined"){div.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1";support.inlineBlockNeedsLayout=val=div.offsetWidth===3;if(val){body.style.zoom=1}}body.removeChild(container)});(function(){var div=document.createElement("div");support.deleteExpando=true;try{delete div.test}catch(e){support.deleteExpando=false}div=null})();var acceptData=function(elem){var noData=jQuery.noData[(elem.nodeName+" ").toLowerCase()],nodeType=+elem.nodeType||1;return nodeType!==1&&nodeType!==9?false:!noData||noData!==true&&elem.getAttribute("classid")===noData};var rbrace=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,rmultiDash=/([A-Z])/g;function dataAttr(elem,key,data){if(data===undefined&&elem.nodeType===1){var name="data-"+key.replace(rmultiDash,"-$1").toLowerCase();data=elem.getAttribute(name);if(typeof data==="string"){try{data=data==="true"?true:data==="false"?false:data==="null"?null:+data+""===data?+data:rbrace.test(data)?jQuery.parseJSON(data):data}catch(e){}jQuery.data(elem,key,data)}else{data=undefined}}return data}function isEmptyDataObject(obj){var name;for(name in obj){if(name==="data"&&jQuery.isEmptyObject(obj[name])){continue}if(name!=="toJSON"){return false}}return true}function internalData(elem,name,data,pvt){if(!acceptData(elem)){return}var ret,thisCache,internalKey=jQuery.expando,isNode=elem.nodeType,cache=isNode?jQuery.cache:elem,id=isNode?elem[internalKey]:elem[internalKey]&&internalKey;if((!id||!cache[id]||!pvt&&!cache[id].data)&&data===undefined&&typeof name==="string"){return}if(!id){if(isNode){id=elem[internalKey]=deletedIds.pop()||jQuery.guid++}else{id=internalKey}}if(!cache[id]){cache[id]=isNode?{}:{toJSON:jQuery.noop}}if(typeof name==="object"||typeof name==="function"){if(pvt){cache[id]=jQuery.extend(cache[id],name)}else{cache[id].data=jQuery.extend(cache[id].data,name)}}thisCache=cache[id];if(!pvt){if(!thisCache.data){thisCache.data={}}thisCache=thisCache.data}if(data!==undefined){thisCache[jQuery.camelCase(name)]=data}if(typeof name==="string"){ret=thisCache[name];if(ret==null){ret=thisCache[jQuery.camelCase(name)]}}else{ret=thisCache}return ret}function internalRemoveData(elem,name,pvt){if(!acceptData(elem)){return}var thisCache,i,isNode=elem.nodeType,cache=isNode?jQuery.cache:elem,id=isNode?elem[jQuery.expando]:jQuery.expando;if(!cache[id]){return}if(name){thisCache=pvt?cache[id]:cache[id].data;if(thisCache){if(!jQuery.isArray(name)){if(name in thisCache){name=[name]}else{name=jQuery.camelCase(name);if(name in thisCache){name=[name]}else{name=name.split(" ")}}}else{name=name.concat(jQuery.map(name,jQuery.camelCase))}i=name.length;while(i--){delete thisCache[name[i]]}if(pvt?!isEmptyDataObject(thisCache):!jQuery.isEmptyObject(thisCache)){return}}}if(!pvt){delete cache[id].data;if(!isEmptyDataObject(cache[id])){return}}if(isNode){jQuery.cleanData([elem],true)}else if(support.deleteExpando||cache!=cache.window){delete cache[id]}else{cache[id]=undefined}}jQuery.extend({cache:{},noData:{"applet ":true,"embed ":true,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(elem){elem=elem.nodeType?jQuery.cache[elem[jQuery.expando]]:elem[jQuery.expando];return!!elem&&!isEmptyDataObject(elem)},data:function(elem,name,data){return internalData(elem,name,data)},removeData:function(elem,name){return internalRemoveData(elem,name)},_data:function(elem,name,data){return internalData(elem,name,data,true)},_removeData:function(elem,name){return internalRemoveData(elem,name,true)}});jQuery.fn.extend({data:function(key,value){var i,name,data,elem=this[0],attrs=elem&&elem.attributes;if(key===undefined){if(this.length){data=jQuery.data(elem);if(elem.nodeType===1&&!jQuery._data(elem,"parsedAttrs")){i=attrs.length;while(i--){if(attrs[i]){name=attrs[i].name;if(name.indexOf("data-")===0){name=jQuery.camelCase(name.slice(5));dataAttr(elem,name,data[name])}}}jQuery._data(elem,"parsedAttrs",true)}}return data}if(typeof key==="object"){return this.each(function(){jQuery.data(this,key)})}return arguments.length>1?this.each(function(){jQuery.data(this,key,value)}):elem?dataAttr(elem,key,jQuery.data(elem,key)):undefined},removeData:function(key){return this.each(function(){jQuery.removeData(this,key)})}});jQuery.extend({queue:function(elem,type,data){var queue;if(elem){type=(type||"fx")+"queue";queue=jQuery._data(elem,type);if(data){if(!queue||jQuery.isArray(data)){queue=jQuery._data(elem,type,jQuery.makeArray(data))}else{queue.push(data)}}return queue||[]}},dequeue:function(elem,type){type=type||"fx";var queue=jQuery.queue(elem,type),startLength=queue.length,fn=queue.shift(),hooks=jQuery._queueHooks(elem,type),next=function(){jQuery.dequeue(elem,type)};if(fn==="inprogress"){fn=queue.shift();startLength--}if(fn){if(type==="fx"){queue.unshift("inprogress")}delete hooks.stop;fn.call(elem,next,hooks)}if(!startLength&&hooks){hooks.empty.fire()}},_queueHooks:function(elem,type){var key=type+"queueHooks";return jQuery._data(elem,key)||jQuery._data(elem,key,{empty:jQuery.Callbacks("once memory").add(function(){jQuery._removeData(elem,type+"queue");jQuery._removeData(elem,key)})})}});jQuery.fn.extend({queue:function(type,data){var setter=2;if(typeof type!=="string"){data=type;type="fx";setter--}if(arguments.length
a";support.leadingWhitespace=div.firstChild.nodeType===3;support.tbody=!div.getElementsByTagName("tbody").length;support.htmlSerialize=!!div.getElementsByTagName("link").length;support.html5Clone=document.createElement("nav").cloneNode(true).outerHTML!=="<:nav>";input.type="checkbox";input.checked=true;fragment.appendChild(input);support.appendChecked=input.checked;div.innerHTML="";support.noCloneChecked=!!div.cloneNode(true).lastChild.defaultValue;fragment.appendChild(div);input=document.createElement("input");input.setAttribute("type","radio");input.setAttribute("checked","checked");input.setAttribute("name","t");div.appendChild(input);support.checkClone=div.cloneNode(true).cloneNode(true).lastChild.checked;support.noCloneEvent=!!div.addEventListener;div[jQuery.expando]=1;support.attributes=!div.getAttribute(jQuery.expando)})();var wrapMap={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:support.htmlSerialize?[0,"",""]:[1,"X
","
"]};wrapMap.optgroup=wrapMap.option;wrapMap.tbody=wrapMap.tfoot=wrapMap.colgroup=wrapMap.caption=wrapMap.thead;wrapMap.th=wrapMap.td;function getAll(context,tag){var elems,elem,i=0,found=typeof context.getElementsByTagName!=="undefined"?context.getElementsByTagName(tag||"*"):typeof context.querySelectorAll!=="undefined"?context.querySelectorAll(tag||"*"):undefined;if(!found){for(found=[],elems=context.childNodes||context;(elem=elems[i])!=null;i++){if(!tag||jQuery.nodeName(elem,tag)){found.push(elem)}else{jQuery.merge(found,getAll(elem,tag))}}}return tag===undefined||tag&&jQuery.nodeName(context,tag)?jQuery.merge([context],found):found}function setGlobalEval(elems,refElements){var elem,i=0;for(;(elem=elems[i])!=null;i++){jQuery._data(elem,"globalEval",!refElements||jQuery._data(refElements[i],"globalEval"))}}var rhtml=/<|&#?\w+;/,rtbody=/"&&!rtbody.test(elem)?tmp:0;j=elem&&elem.childNodes.length;while(j--){if(jQuery.nodeName(tbody=elem.childNodes[j],"tbody")&&!tbody.childNodes.length){elem.removeChild(tbody)}}}jQuery.merge(nodes,tmp.childNodes);tmp.textContent="";while(tmp.firstChild){tmp.removeChild(tmp.firstChild)}tmp=safe.lastChild}}}if(tmp){safe.removeChild(tmp)}if(!support.appendChecked){jQuery.grep(getAll(nodes,"input"),fixDefaultChecked)}i=0;while(elem=nodes[i++]){if(selection&&jQuery.inArray(elem,selection)>-1){if(ignored){ignored.push(elem)}continue}contains=jQuery.contains(elem.ownerDocument,elem);tmp=getAll(safe.appendChild(elem),"script");if(contains){setGlobalEval(tmp)}if(scripts){j=0;while(elem=tmp[j++]){if(rscriptType.test(elem.type||"")){scripts.push(elem)}}}}tmp=null;return safe}(function(){var i,eventName,div=document.createElement("div");for(i in{submit:true,change:true,focusin:true}){eventName="on"+i;if(!(support[i]=eventName in window)){div.setAttribute(eventName,"t");support[i]=div.attributes[eventName].expando===false}}div=null})();var rformElems=/^(?:input|select|textarea)$/i,rkeyEvent=/^key/,rmouseEvent=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,rfocusMorph=/^(?:focusinfocus|focusoutblur)$/,rtypenamespace=/^([^.]*)(?:\.(.+)|)/;function returnTrue(){return true}function returnFalse(){return false}function safeActiveElement(){try{return document.activeElement}catch(err){}}function on(elem,types,selector,data,fn,one){var origFn,type;if(typeof types==="object"){if(typeof selector!=="string"){data=data||selector;selector=undefined}for(type in types){on(elem,type,selector,data,types[type],one)}return elem}if(data==null&&fn==null){fn=selector;data=selector=undefined}else if(fn==null){if(typeof selector==="string"){fn=data;data=undefined}else{fn=data;data=selector;selector=undefined}}if(fn===false){fn=returnFalse}else if(!fn){return elem}if(one===1){origFn=fn;fn=function(event){jQuery().off(event);return origFn.apply(this,arguments)};fn.guid=origFn.guid||(origFn.guid=jQuery.guid++)}return elem.each(function(){jQuery.event.add(this,types,fn,data,selector)})}jQuery.event={global:{},add:function(elem,types,handler,data,selector){var tmp,events,t,handleObjIn,special,eventHandle,handleObj,handlers,type,namespaces,origType,elemData=jQuery._data(elem);if(!elemData){return}if(handler.handler){handleObjIn=handler;handler=handleObjIn.handler;selector=handleObjIn.selector}if(!handler.guid){handler.guid=jQuery.guid++}if(!(events=elemData.events)){events=elemData.events={}}if(!(eventHandle=elemData.handle)){eventHandle=elemData.handle=function(e){return typeof jQuery!=="undefined"&&(!e||jQuery.event.triggered!==e.type)?jQuery.event.dispatch.apply(eventHandle.elem,arguments):undefined};eventHandle.elem=elem}types=(types||"").match(rnotwhite)||[""];t=types.length;while(t--){tmp=rtypenamespace.exec(types[t])||[];type=origType=tmp[1];namespaces=(tmp[2]||"").split(".").sort();if(!type){continue}special=jQuery.event.special[type]||{};type=(selector?special.delegateType:special.bindType)||type;special=jQuery.event.special[type]||{};handleObj=jQuery.extend({type:type,origType:origType,data:data,handler:handler,guid:handler.guid,selector:selector,needsContext:selector&&jQuery.expr.match.needsContext.test(selector),namespace:namespaces.join(".")},handleObjIn);if(!(handlers=events[type])){handlers=events[type]=[];handlers.delegateCount=0;if(!special.setup||special.setup.call(elem,data,namespaces,eventHandle)===false){if(elem.addEventListener){elem.addEventListener(type,eventHandle,false)}else if(elem.attachEvent){elem.attachEvent("on"+type,eventHandle)}}}if(special.add){special.add.call(elem,handleObj);if(!handleObj.handler.guid){handleObj.handler.guid=handler.guid}}if(selector){handlers.splice(handlers.delegateCount++,0,handleObj)}else{handlers.push(handleObj)}jQuery.event.global[type]=true}elem=null},remove:function(elem,types,handler,selector,mappedTypes){var j,handleObj,tmp,origCount,t,events,special,handlers,type,namespaces,origType,elemData=jQuery.hasData(elem)&&jQuery._data(elem);if(!elemData||!(events=elemData.events)){return}types=(types||"").match(rnotwhite)||[""];t=types.length;while(t--){tmp=rtypenamespace.exec(types[t])||[];type=origType=tmp[1];namespaces=(tmp[2]||"").split(".").sort();if(!type){for(type in events){jQuery.event.remove(elem,type+types[t],handler,selector,true)}continue}special=jQuery.event.special[type]||{};type=(selector?special.delegateType:special.bindType)||type;handlers=events[type]||[];tmp=tmp[2]&&new RegExp("(^|\\.)"+namespaces.join("\\.(?:.*\\.|)")+"(\\.|$)");origCount=j=handlers.length;while(j--){handleObj=handlers[j];if((mappedTypes||origType===handleObj.origType)&&(!handler||handler.guid===handleObj.guid)&&(!tmp||tmp.test(handleObj.namespace))&&(!selector||selector===handleObj.selector||selector==="**"&&handleObj.selector)){handlers.splice(j,1);if(handleObj.selector){handlers.delegateCount--}if(special.remove){special.remove.call(elem,handleObj)}}}if(origCount&&!handlers.length){if(!special.teardown||special.teardown.call(elem,namespaces,elemData.handle)===false){jQuery.removeEvent(elem,type,elemData.handle)}delete events[type]}}if(jQuery.isEmptyObject(events)){delete elemData.handle;jQuery._removeData(elem,"events")}},trigger:function(event,data,elem,onlyHandlers){var handle,ontype,cur,bubbleType,special,tmp,i,eventPath=[elem||document],type=hasOwn.call(event,"type")?event.type:event,namespaces=hasOwn.call(event,"namespace")?event.namespace.split("."):[];cur=tmp=elem=elem||document;if(elem.nodeType===3||elem.nodeType===8){return}if(rfocusMorph.test(type+jQuery.event.triggered)){return}if(type.indexOf(".")>-1){namespaces=type.split(".");type=namespaces.shift();namespaces.sort()}ontype=type.indexOf(":")<0&&"on"+type;event=event[jQuery.expando]?event:new jQuery.Event(type,typeof event==="object"&&event);event.isTrigger=onlyHandlers?2:3;event.namespace=namespaces.join(".");event.rnamespace=event.namespace?new RegExp("(^|\\.)"+namespaces.join("\\.(?:.*\\.|)")+"(\\.|$)"):null;event.result=undefined;if(!event.target){event.target=elem}data=data==null?[event]:jQuery.makeArray(data,[event]);special=jQuery.event.special[type]||{};if(!onlyHandlers&&special.trigger&&special.trigger.apply(elem,data)===false){return}if(!onlyHandlers&&!special.noBubble&&!jQuery.isWindow(elem)){bubbleType=special.delegateType||type;if(!rfocusMorph.test(bubbleType+type)){cur=cur.parentNode}for(;cur;cur=cur.parentNode){eventPath.push(cur);tmp=cur}if(tmp===(elem.ownerDocument||document)){eventPath.push(tmp.defaultView||tmp.parentWindow||window)}}i=0;while((cur=eventPath[i++])&&!event.isPropagationStopped()){event.type=i>1?bubbleType:special.bindType||type;handle=(jQuery._data(cur,"events")||{})[event.type]&&jQuery._data(cur,"handle");if(handle){handle.apply(cur,data)}handle=ontype&&cur[ontype];if(handle&&handle.apply&&acceptData(cur)){event.result=handle.apply(cur,data);if(event.result===false){event.preventDefault()}}}event.type=type;if(!onlyHandlers&&!event.isDefaultPrevented()){if((!special._default||special._default.apply(eventPath.pop(),data)===false)&&acceptData(elem)){if(ontype&&elem[type]&&!jQuery.isWindow(elem)){tmp=elem[ontype];if(tmp){elem[ontype]=null}jQuery.event.triggered=type;try{elem[type]()}catch(e){}jQuery.event.triggered=undefined;if(tmp){elem[ontype]=tmp}}}}return event.result},dispatch:function(event){event=jQuery.event.fix(event);var i,j,ret,matched,handleObj,handlerQueue=[],args=slice.call(arguments),handlers=(jQuery._data(this,"events")||{})[event.type]||[],special=jQuery.event.special[event.type]||{};args[0]=event;event.delegateTarget=this;if(special.preDispatch&&special.preDispatch.call(this,event)===false){return}handlerQueue=jQuery.event.handlers.call(this,event,handlers);i=0;while((matched=handlerQueue[i++])&&!event.isPropagationStopped()){event.currentTarget=matched.elem;j=0;while((handleObj=matched.handlers[j++])&&!event.isImmediatePropagationStopped()){if(!event.rnamespace||event.rnamespace.test(handleObj.namespace)){event.handleObj=handleObj;event.data=handleObj.data;ret=((jQuery.event.special[handleObj.origType]||{}).handle||handleObj.handler).apply(matched.elem,args);if(ret!==undefined){if((event.result=ret)===false){event.preventDefault();event.stopPropagation()}}}}}if(special.postDispatch){special.postDispatch.call(this,event)}return event.result},handlers:function(event,handlers){var i,matches,sel,handleObj,handlerQueue=[],delegateCount=handlers.delegateCount,cur=event.target;if(delegateCount&&cur.nodeType&&(event.type!=="click"||isNaN(event.button)||event.button<1)){for(;cur!=this;cur=cur.parentNode||this){if(cur.nodeType===1&&(cur.disabled!==true||event.type!=="click")){matches=[];for(i=0;i-1:jQuery.find(sel,this,null,[cur]).length}if(matches[sel]){matches.push(handleObj)}}if(matches.length){handlerQueue.push({elem:cur,handlers:matches})}}}}if(delegateCount]","i"),rxhtmlTag=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,rnoInnerhtml=/\s*$/g,safeFragment=createSafeFragment(document),fragmentDiv=safeFragment.appendChild(document.createElement("div"));function manipulationTarget(elem,content){return jQuery.nodeName(elem,"table")&&jQuery.nodeName(content.nodeType!==11?content:content.firstChild,"tr")?elem.getElementsByTagName("tbody")[0]||elem.appendChild(elem.ownerDocument.createElement("tbody")):elem}function disableScript(elem){elem.type=(jQuery.find.attr(elem,"type")!==null)+"/"+elem.type;return elem}function restoreScript(elem){var match=rscriptTypeMasked.exec(elem.type);if(match){elem.type=match[1]}else{elem.removeAttribute("type")}return elem}function cloneCopyEvent(src,dest){if(dest.nodeType!==1||!jQuery.hasData(src)){return}var type,i,l,oldData=jQuery._data(src),curData=jQuery._data(dest,oldData),events=oldData.events;if(events){delete curData.handle;curData.events={};for(type in events){for(i=0,l=events[type].length;i1&&typeof value==="string"&&!support.checkClone&&rchecked.test(value)){return collection.each(function(index){var self=collection.eq(index);if(isFunction){args[0]=value.call(this,index,self.html())}domManip(self,args,callback,ignored)})}if(l){fragment=buildFragment(args,collection[0].ownerDocument,false,collection,ignored);first=fragment.firstChild;if(fragment.childNodes.length===1){fragment=first}if(first||ignored){scripts=jQuery.map(getAll(fragment,"script"),disableScript);hasScripts=scripts.length;for(;i")},clone:function(elem,dataAndEvents,deepDataAndEvents){var destElements,node,clone,i,srcElements,inPage=jQuery.contains(elem.ownerDocument,elem);if(support.html5Clone||jQuery.isXMLDoc(elem)||!rnoshimcache.test("<"+elem.nodeName+">")){clone=elem.cloneNode(true)}else{fragmentDiv.innerHTML=elem.outerHTML;fragmentDiv.removeChild(clone=fragmentDiv.firstChild)}if((!support.noCloneEvent||!support.noCloneChecked)&&(elem.nodeType===1||elem.nodeType===11)&&!jQuery.isXMLDoc(elem)){destElements=getAll(clone);srcElements=getAll(elem);for(i=0;(node=srcElements[i])!=null;++i){if(destElements[i]){fixCloneNodeIssues(node,destElements[i])}}}if(dataAndEvents){if(deepDataAndEvents){srcElements=srcElements||getAll(elem);destElements=destElements||getAll(clone);for(i=0;(node=srcElements[i])!=null;i++){cloneCopyEvent(node,destElements[i])}}else{cloneCopyEvent(elem,clone)}}destElements=getAll(clone,"script");if(destElements.length>0){setGlobalEval(destElements,!inPage&&getAll(elem,"script"))}destElements=srcElements=node=null;return clone},cleanData:function(elems,forceAcceptData){var elem,type,id,data,i=0,internalKey=jQuery.expando,cache=jQuery.cache,attributes=support.attributes,special=jQuery.event.special;for(;(elem=elems[i])!=null;i++){if(forceAcceptData||acceptData(elem)){id=elem[internalKey];data=id&&cache[id];if(data){if(data.events){for(type in data.events){if(special[type]){jQuery.event.remove(elem,type)}else{jQuery.removeEvent(elem,type,data.handle)}}}if(cache[id]){delete cache[id];if(!attributes&&typeof elem.removeAttribute!=="undefined"){elem.removeAttribute(internalKey)}else{elem[internalKey]=undefined}deletedIds.push(id)}}}}}});jQuery.fn.extend({domManip:domManip,detach:function(selector){return remove(this,selector,true)},remove:function(selector){return remove(this,selector)},text:function(value){return access(this,function(value){return value===undefined?jQuery.text(this):this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(value))},null,value,arguments.length)},append:function(){return domManip(this,arguments,function(elem){if(this.nodeType===1||this.nodeType===11||this.nodeType===9){var target=manipulationTarget(this,elem);target.appendChild(elem)}})},prepend:function(){return domManip(this,arguments,function(elem){if(this.nodeType===1||this.nodeType===11||this.nodeType===9){var target=manipulationTarget(this,elem);target.insertBefore(elem,target.firstChild)}})},before:function(){return domManip(this,arguments,function(elem){if(this.parentNode){this.parentNode.insertBefore(elem,this)}})},after:function(){return domManip(this,arguments,function(elem){if(this.parentNode){this.parentNode.insertBefore(elem,this.nextSibling)}})},empty:function(){var elem,i=0;for(;(elem=this[i])!=null;i++){if(elem.nodeType===1){jQuery.cleanData(getAll(elem,false))}while(elem.firstChild){elem.removeChild(elem.firstChild)}if(elem.options&&jQuery.nodeName(elem,"select")){elem.options.length=0}}return this},clone:function(dataAndEvents,deepDataAndEvents){dataAndEvents=dataAndEvents==null?false:dataAndEvents;deepDataAndEvents=deepDataAndEvents==null?dataAndEvents:deepDataAndEvents;return this.map(function(){return jQuery.clone(this,dataAndEvents,deepDataAndEvents)})},html:function(value){return access(this,function(value){var elem=this[0]||{},i=0,l=this.length;if(value===undefined){return elem.nodeType===1?elem.innerHTML.replace(rinlinejQuery,""):undefined}if(typeof value==="string"&&!rnoInnerhtml.test(value)&&(support.htmlSerialize||!rnoshimcache.test(value))&&(support.leadingWhitespace||!rleadingWhitespace.test(value))&&!wrapMap[(rtagName.exec(value)||["",""])[1].toLowerCase()]){value=jQuery.htmlPrefilter(value);try{for(;i")).appendTo(doc.documentElement);doc=(iframe[0].contentWindow||iframe[0].contentDocument).document;doc.write();doc.close();display=actualDisplay(nodeName,doc);iframe.detach()}elemdisplay[nodeName]=display}return display}var rmargin=/^margin/;var rnumnonpx=new RegExp("^("+pnum+")(?!px)[a-z%]+$","i");var swap=function(elem,options,callback,args){var ret,name,old={};for(name in options){old[name]=elem.style[name];elem.style[name]=options[name]}ret=callback.apply(elem,args||[]);for(name in options){elem.style[name]=old[name]}return ret};var documentElement=document.documentElement;(function(){var pixelPositionVal,pixelMarginRightVal,boxSizingReliableVal,reliableHiddenOffsetsVal,reliableMarginRightVal,reliableMarginLeftVal,container=document.createElement("div"),div=document.createElement("div");if(!div.style){return}div.style.cssText="float:left;opacity:.5";support.opacity=div.style.opacity==="0.5";support.cssFloat=!!div.style.cssFloat;div.style.backgroundClip="content-box";div.cloneNode(true).style.backgroundClip="";support.clearCloneStyle=div.style.backgroundClip==="content-box";container=document.createElement("div");container.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;"+"padding:0;margin-top:1px;position:absolute";div.innerHTML="";container.appendChild(div);support.boxSizing=div.style.boxSizing===""||div.style.MozBoxSizing===""||div.style.WebkitBoxSizing==="";jQuery.extend(support,{reliableHiddenOffsets:function(){if(pixelPositionVal==null){computeStyleTests()}return reliableHiddenOffsetsVal},boxSizingReliable:function(){if(pixelPositionVal==null){computeStyleTests()}return boxSizingReliableVal},pixelMarginRight:function(){if(pixelPositionVal==null){computeStyleTests()}return pixelMarginRightVal},pixelPosition:function(){if(pixelPositionVal==null){computeStyleTests()}return pixelPositionVal},reliableMarginRight:function(){if(pixelPositionVal==null){computeStyleTests()}return reliableMarginRightVal},reliableMarginLeft:function(){if(pixelPositionVal==null){computeStyleTests()}return reliableMarginLeftVal}});function computeStyleTests(){var contents,divStyle,documentElement=document.documentElement;documentElement.appendChild(container);div.style.cssText="-webkit-box-sizing:border-box;box-sizing:border-box;"+"position:relative;display:block;"+"margin:auto;border:1px;padding:1px;"+"top:1%;width:50%";pixelPositionVal=boxSizingReliableVal=reliableMarginLeftVal=false;pixelMarginRightVal=reliableMarginRightVal=true;if(window.getComputedStyle){divStyle=window.getComputedStyle(div);pixelPositionVal=(divStyle||{}).top!=="1%";reliableMarginLeftVal=(divStyle||{}).marginLeft==="2px";boxSizingReliableVal=(divStyle||{width:"4px"}).width==="4px";div.style.marginRight="50%";pixelMarginRightVal=(divStyle||{marginRight:"4px"}).marginRight==="4px";contents=div.appendChild(document.createElement("div"));contents.style.cssText=div.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;"+"box-sizing:content-box;display:block;margin:0;border:0;padding:0";contents.style.marginRight=contents.style.width="0";div.style.width="1px";reliableMarginRightVal=!parseFloat((window.getComputedStyle(contents)||{}).marginRight);div.removeChild(contents)}div.style.display="none";reliableHiddenOffsetsVal=div.getClientRects().length===0;if(reliableHiddenOffsetsVal){div.style.display="";div.innerHTML="
t
";contents=div.getElementsByTagName("td");contents[0].style.cssText="margin:0;border:0;padding:0;display:none";reliableHiddenOffsetsVal=contents[0].offsetHeight===0;if(reliableHiddenOffsetsVal){contents[0].style.display="";contents[1].style.display="none";reliableHiddenOffsetsVal=contents[0].offsetHeight===0}}documentElement.removeChild(container)}})();var getStyles,curCSS,rposition=/^(top|right|bottom|left)$/;if(window.getComputedStyle){getStyles=function(elem){var view=elem.ownerDocument.defaultView;if(!view||!view.opener){view=window}return view.getComputedStyle(elem)};curCSS=function(elem,name,computed){var width,minWidth,maxWidth,ret,style=elem.style;computed=computed||getStyles(elem);ret=computed?computed.getPropertyValue(name)||computed[name]:undefined;if((ret===""||ret===undefined)&&!jQuery.contains(elem.ownerDocument,elem)){ret=jQuery.style(elem,name)}if(computed){if(!support.pixelMarginRight()&&rnumnonpx.test(ret)&&rmargin.test(name)){width=style.width;minWidth=style.minWidth;maxWidth=style.maxWidth;style.minWidth=style.maxWidth=style.width=ret;ret=computed.width;style.width=width;style.minWidth=minWidth;style.maxWidth=maxWidth}}return ret===undefined?ret:ret+""}}else if(documentElement.currentStyle){getStyles=function(elem){return elem.currentStyle};curCSS=function(elem,name,computed){var left,rs,rsLeft,ret,style=elem.style;computed=computed||getStyles(elem);ret=computed?computed[name]:undefined;if(ret==null&&style&&style[name]){ret=style[name]}if(rnumnonpx.test(ret)&&!rposition.test(name)){left=style.left;rs=elem.runtimeStyle;rsLeft=rs&&rs.left;if(rsLeft){rs.left=elem.currentStyle.left}style.left=name==="fontSize"?"1em":ret;ret=style.pixelLeft+"px";style.left=left;if(rsLeft){rs.left=rsLeft}}return ret===undefined?ret:ret+""||"auto"}}function addGetHookIf(conditionFn,hookFn){return{get:function(){if(conditionFn()){delete this.get;return}return(this.get=hookFn).apply(this,arguments)}}}var ralpha=/alpha\([^)]*\)/i,ropacity=/opacity\s*=\s*([^)]*)/i,rdisplayswap=/^(none|table(?!-c[ea]).+)/,rnumsplit=new RegExp("^("+pnum+")(.*)$","i"),cssShow={position:"absolute",visibility:"hidden",display:"block"},cssNormalTransform={letterSpacing:"0",fontWeight:"400"},cssPrefixes=["Webkit","O","Moz","ms"],emptyStyle=document.createElement("div").style;function vendorPropName(name){if(name in emptyStyle){return name}var capName=name.charAt(0).toUpperCase()+name.slice(1),i=cssPrefixes.length;while(i--){name=cssPrefixes[i]+capName;if(name in emptyStyle){return name}}}function showHide(elements,show){var display,elem,hidden,values=[],index=0,length=elements.length;for(;index=1||value==="")&&jQuery.trim(filter.replace(ralpha,""))===""&&style.removeAttribute){style.removeAttribute("filter");if(value===""||currentStyle&&!currentStyle.filter){return}}style.filter=ralpha.test(filter)?filter.replace(ralpha,opacity):filter+" "+opacity}}}jQuery.cssHooks.marginRight=addGetHookIf(support.reliableMarginRight,function(elem,computed){if(computed){return swap(elem,{display:"inline-block"},curCSS,[elem,"marginRight"])}});jQuery.cssHooks.marginLeft=addGetHookIf(support.reliableMarginLeft,function(elem,computed){if(computed){return(parseFloat(curCSS(elem,"marginLeft"))||(jQuery.contains(elem.ownerDocument,elem)?elem.getBoundingClientRect().left-swap(elem,{marginLeft:0},function(){return elem.getBoundingClientRect().left}):0))+"px"}});jQuery.each({margin:"",padding:"",border:"Width"},function(prefix,suffix){jQuery.cssHooks[prefix+suffix]={expand:function(value){var i=0,expanded={},parts=typeof value==="string"?value.split(" "):[value];for(;i<4;i++){expanded[prefix+cssExpand[i]+suffix]=parts[i]||parts[i-2]||parts[0]}return expanded}};if(!rmargin.test(prefix)){jQuery.cssHooks[prefix+suffix].set=setPositiveNumber}});jQuery.fn.extend({css:function(name,value){return access(this,function(elem,name,value){var styles,len,map={},i=0;if(jQuery.isArray(name)){styles=getStyles(elem);len=name.length;for(;i1)},show:function(){return showHide(this,true)},hide:function(){return showHide(this)},toggle:function(state){if(typeof state==="boolean"){return state?this.show():this.hide()}return this.each(function(){if(isHidden(this)){jQuery(this).show()}else{jQuery(this).hide()}})}});function Tween(elem,options,prop,end,easing){return new Tween.prototype.init(elem,options,prop,end,easing)}jQuery.Tween=Tween;Tween.prototype={constructor:Tween,init:function(elem,options,prop,end,easing,unit){this.elem=elem;this.prop=prop;this.easing=easing||jQuery.easing._default;this.options=options;this.start=this.now=this.cur();this.end=end;this.unit=unit||(jQuery.cssNumber[prop]?"":"px")},cur:function(){var hooks=Tween.propHooks[this.prop];return hooks&&hooks.get?hooks.get(this):Tween.propHooks._default.get(this)},run:function(percent){var eased,hooks=Tween.propHooks[this.prop];if(this.options.duration){this.pos=eased=jQuery.easing[this.easing](percent,this.options.duration*percent,0,1,this.options.duration)}else{this.pos=eased=percent}this.now=(this.end-this.start)*eased+this.start;if(this.options.step){this.options.step.call(this.elem,this.now,this)}if(hooks&&hooks.set){hooks.set(this)}else{Tween.propHooks._default.set(this)}return this}};Tween.prototype.init.prototype=Tween.prototype;Tween.propHooks={_default:{get:function(tween){var result;if(tween.elem.nodeType!==1||tween.elem[tween.prop]!=null&&tween.elem.style[tween.prop]==null){return tween.elem[tween.prop]}result=jQuery.css(tween.elem,tween.prop,"");return!result||result==="auto"?0:result},set:function(tween){if(jQuery.fx.step[tween.prop]){jQuery.fx.step[tween.prop](tween)}else if(tween.elem.nodeType===1&&(tween.elem.style[jQuery.cssProps[tween.prop]]!=null||jQuery.cssHooks[tween.prop])){jQuery.style(tween.elem,tween.prop,tween.now+tween.unit)}else{tween.elem[tween.prop]=tween.now}}}};Tween.propHooks.scrollTop=Tween.propHooks.scrollLeft={set:function(tween){if(tween.elem.nodeType&&tween.elem.parentNode){tween.elem[tween.prop]=tween.now}}};jQuery.easing={linear:function(p){return p},swing:function(p){return.5-Math.cos(p*Math.PI)/2},_default:"swing"};jQuery.fx=Tween.prototype.init;jQuery.fx.step={};var fxNow,timerId,rfxtypes=/^(?:toggle|show|hide)$/,rrun=/queueHooks$/;function createFxNow(){window.setTimeout(function(){fxNow=undefined});return fxNow=jQuery.now()}function genFx(type,includeWidth){var which,attrs={height:type},i=0;includeWidth=includeWidth?1:0;for(;i<4;i+=2-includeWidth){which=cssExpand[i];attrs["margin"+which]=attrs["padding"+which]=type}if(includeWidth){attrs.opacity=attrs.width=type}return attrs}function createTween(value,prop,animation){var tween,collection=(Animation.tweeners[prop]||[]).concat(Animation.tweeners["*"]),index=0,length=collection.length;for(;index
a";a=div.getElementsByTagName("a")[0];input.setAttribute("type","checkbox");div.appendChild(input);a=div.getElementsByTagName("a")[0];a.style.cssText="top:1px";support.getSetAttribute=div.className!=="t";support.style=/top/.test(a.getAttribute("style"));support.hrefNormalized=a.getAttribute("href")==="/a";support.checkOn=!!input.value;support.optSelected=opt.selected;support.enctype=!!document.createElement("form").enctype;select.disabled=true;support.optDisabled=!opt.disabled;input=document.createElement("input");input.setAttribute("value","");support.input=input.getAttribute("value")==="";input.value="t";input.setAttribute("type","radio");support.radioValue=input.value==="t"})();var rreturn=/\r/g;jQuery.fn.extend({val:function(value){var hooks,ret,isFunction,elem=this[0];if(!arguments.length){if(elem){hooks=jQuery.valHooks[elem.type]||jQuery.valHooks[elem.nodeName.toLowerCase()];if(hooks&&"get"in hooks&&(ret=hooks.get(elem,"value"))!==undefined){return ret}ret=elem.value;return typeof ret==="string"?ret.replace(rreturn,""):ret==null?"":ret}return}isFunction=jQuery.isFunction(value);return this.each(function(i){var val;if(this.nodeType!==1){return}if(isFunction){val=value.call(this,i,jQuery(this).val())}else{val=value}if(val==null){val=""}else if(typeof val==="number"){val+=""}else if(jQuery.isArray(val)){val=jQuery.map(val,function(value){return value==null?"":value+""})}hooks=jQuery.valHooks[this.type]||jQuery.valHooks[this.nodeName.toLowerCase()];if(!hooks||!("set"in hooks)||hooks.set(this,val,"value")===undefined){this.value=val}})}});jQuery.extend({valHooks:{option:{get:function(elem){var val=jQuery.find.attr(elem,"value");return val!=null?val:jQuery.trim(jQuery.text(elem))}},select:{get:function(elem){var value,option,options=elem.options,index=elem.selectedIndex,one=elem.type==="select-one"||index<0,values=one?null:[],max=one?index+1:options.length,i=index<0?max:one?index:0;for(;i=0){try{option.selected=optionSet=true}catch(_){option.scrollHeight}}else{option.selected=false}}if(!optionSet){elem.selectedIndex=-1}return options}}}});jQuery.each(["radio","checkbox"],function(){jQuery.valHooks[this]={set:function(elem,value){if(jQuery.isArray(value)){return elem.checked=jQuery.inArray(jQuery(elem).val(),value)>-1}}};if(!support.checkOn){jQuery.valHooks[this].get=function(elem){return elem.getAttribute("value")===null?"on":elem.value}}});var nodeHook,boolHook,attrHandle=jQuery.expr.attrHandle,ruseDefault=/^(?:checked|selected)$/i,getSetAttribute=support.getSetAttribute,getSetInput=support.input;jQuery.fn.extend({attr:function(name,value){return access(this,jQuery.attr,name,value,arguments.length>1)},removeAttr:function(name){return this.each(function(){jQuery.removeAttr(this,name)})}});jQuery.extend({attr:function(elem,name,value){var ret,hooks,nType=elem.nodeType;if(nType===3||nType===8||nType===2){return}if(typeof elem.getAttribute==="undefined"){return jQuery.prop(elem,name,value)}if(nType!==1||!jQuery.isXMLDoc(elem)){name=name.toLowerCase();hooks=jQuery.attrHooks[name]||(jQuery.expr.match.bool.test(name)?boolHook:nodeHook)}if(value!==undefined){if(value===null){jQuery.removeAttr(elem,name);return}if(hooks&&"set"in hooks&&(ret=hooks.set(elem,value,name))!==undefined){return ret}elem.setAttribute(name,value+"");return value}if(hooks&&"get"in hooks&&(ret=hooks.get(elem,name))!==null){return ret}ret=jQuery.find.attr(elem,name);return ret==null?undefined:ret},attrHooks:{type:{set:function(elem,value){if(!support.radioValue&&value==="radio"&&jQuery.nodeName(elem,"input")){var val=elem.value;elem.setAttribute("type",value);if(val){elem.value=val}return value}}}},removeAttr:function(elem,value){var name,propName,i=0,attrNames=value&&value.match(rnotwhite);if(attrNames&&elem.nodeType===1){while(name=attrNames[i++]){propName=jQuery.propFix[name]||name;if(jQuery.expr.match.bool.test(name)){if(getSetInput&&getSetAttribute||!ruseDefault.test(name)){elem[propName]=false}else{elem[jQuery.camelCase("default-"+name)]=elem[propName]=false}}else{jQuery.attr(elem,name,"")}elem.removeAttribute(getSetAttribute?name:propName)}}}});boolHook={set:function(elem,value,name){if(value===false){jQuery.removeAttr(elem,name)}else if(getSetInput&&getSetAttribute||!ruseDefault.test(name)){elem.setAttribute(!getSetAttribute&&jQuery.propFix[name]||name,name)}else{elem[jQuery.camelCase("default-"+name)]=elem[name]=true}return name}};jQuery.each(jQuery.expr.match.bool.source.match(/\w+/g),function(i,name){var getter=attrHandle[name]||jQuery.find.attr;if(getSetInput&&getSetAttribute||!ruseDefault.test(name)){attrHandle[name]=function(elem,name,isXML){var ret,handle;if(!isXML){handle=attrHandle[name];attrHandle[name]=ret;ret=getter(elem,name,isXML)!=null?name.toLowerCase():null;attrHandle[name]=handle}return ret}}else{attrHandle[name]=function(elem,name,isXML){if(!isXML){return elem[jQuery.camelCase("default-"+name)]?name.toLowerCase():null}}}});if(!getSetInput||!getSetAttribute){jQuery.attrHooks.value={set:function(elem,value,name){if(jQuery.nodeName(elem,"input")){elem.defaultValue=value}else{return nodeHook&&nodeHook.set(elem,value,name)}}}}if(!getSetAttribute){nodeHook={set:function(elem,value,name){var ret=elem.getAttributeNode(name);if(!ret){elem.setAttributeNode(ret=elem.ownerDocument.createAttribute(name))}ret.value=value+="";if(name==="value"||value===elem.getAttribute(name)){return value}}};attrHandle.id=attrHandle.name=attrHandle.coords=function(elem,name,isXML){var ret;if(!isXML){return(ret=elem.getAttributeNode(name))&&ret.value!==""?ret.value:null}};jQuery.valHooks.button={get:function(elem,name){var ret=elem.getAttributeNode(name);if(ret&&ret.specified){return ret.value}},set:nodeHook.set};jQuery.attrHooks.contenteditable={set:function(elem,value,name){nodeHook.set(elem,value===""?false:value,name)}};jQuery.each(["width","height"],function(i,name){jQuery.attrHooks[name]={set:function(elem,value){if(value===""){elem.setAttribute(name,"auto");return value}}}})}if(!support.style){jQuery.attrHooks.style={get:function(elem){return elem.style.cssText||undefined},set:function(elem,value){return elem.style.cssText=value+""}}}var rfocusable=/^(?:input|select|textarea|button|object)$/i,rclickable=/^(?:a|area)$/i;jQuery.fn.extend({prop:function(name,value){return access(this,jQuery.prop,name,value,arguments.length>1)},removeProp:function(name){name=jQuery.propFix[name]||name;return this.each(function(){try{this[name]=undefined;delete this[name]}catch(e){}})}});jQuery.extend({prop:function(elem,name,value){var ret,hooks,nType=elem.nodeType;if(nType===3||nType===8||nType===2){return}if(nType!==1||!jQuery.isXMLDoc(elem)){name=jQuery.propFix[name]||name;hooks=jQuery.propHooks[name]}if(value!==undefined){if(hooks&&"set"in hooks&&(ret=hooks.set(elem,value,name))!==undefined){return ret}return elem[name]=value}if(hooks&&"get"in hooks&&(ret=hooks.get(elem,name))!==null){return ret}return elem[name]},propHooks:{tabIndex:{get:function(elem){var tabindex=jQuery.find.attr(elem,"tabindex");return tabindex?parseInt(tabindex,10):rfocusable.test(elem.nodeName)||rclickable.test(elem.nodeName)&&elem.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}});if(!support.hrefNormalized){jQuery.each(["href","src"],function(i,name){jQuery.propHooks[name]={get:function(elem){return elem.getAttribute(name,4)}}})}if(!support.optSelected){jQuery.propHooks.selected={get:function(elem){var parent=elem.parentNode;if(parent){parent.selectedIndex;if(parent.parentNode){parent.parentNode.selectedIndex}}return null}}}jQuery.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){jQuery.propFix[this.toLowerCase()]=this});if(!support.enctype){jQuery.propFix.enctype="encoding"}var rclass=/[\t\r\n\f]/g;function getClass(elem){return jQuery.attr(elem,"class")||""}jQuery.fn.extend({addClass:function(value){var classes,elem,cur,curValue,clazz,j,finalValue,i=0;if(jQuery.isFunction(value)){return this.each(function(j){jQuery(this).addClass(value.call(this,j,getClass(this)))})}if(typeof value==="string"&&value){classes=value.match(rnotwhite)||[];while(elem=this[i++]){curValue=getClass(elem);cur=elem.nodeType===1&&(" "+curValue+" ").replace(rclass," ");if(cur){j=0;while(clazz=classes[j++]){if(cur.indexOf(" "+clazz+" ")<0){cur+=clazz+" "}}finalValue=jQuery.trim(cur);if(curValue!==finalValue){jQuery.attr(elem,"class",finalValue)}}}}return this},removeClass:function(value){var classes,elem,cur,curValue,clazz,j,finalValue,i=0;if(jQuery.isFunction(value)){return this.each(function(j){jQuery(this).removeClass(value.call(this,j,getClass(this)))})}if(!arguments.length){return this.attr("class","")}if(typeof value==="string"&&value){classes=value.match(rnotwhite)||[];while(elem=this[i++]){curValue=getClass(elem);cur=elem.nodeType===1&&(" "+curValue+" ").replace(rclass," ");if(cur){j=0;while(clazz=classes[j++]){while(cur.indexOf(" "+clazz+" ")>-1){cur=cur.replace(" "+clazz+" "," ")}}finalValue=jQuery.trim(cur);if(curValue!==finalValue){jQuery.attr(elem,"class",finalValue)}}}}return this},toggleClass:function(value,stateVal){var type=typeof value;if(typeof stateVal==="boolean"&&type==="string"){return stateVal?this.addClass(value):this.removeClass(value)}if(jQuery.isFunction(value)){return this.each(function(i){jQuery(this).toggleClass(value.call(this,i,getClass(this),stateVal),stateVal)})}return this.each(function(){var className,i,self,classNames;if(type==="string"){i=0;self=jQuery(this);classNames=value.match(rnotwhite)||[];while(className=classNames[i++]){if(self.hasClass(className)){self.removeClass(className)}else{self.addClass(className)}}}else if(value===undefined||type==="boolean"){className=getClass(this);if(className){jQuery._data(this,"__className__",className)}jQuery.attr(this,"class",className||value===false?"":jQuery._data(this,"__className__")||"")}})},hasClass:function(selector){var className,elem,i=0;className=" "+selector+" ";while(elem=this[i++]){if(elem.nodeType===1&&(" "+getClass(elem)+" ").replace(rclass," ").indexOf(className)>-1){return true}}return false}});jQuery.each(("blur focus focusin focusout load resize scroll unload click dblclick "+"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave "+"change select submit keydown keypress keyup error contextmenu").split(" "),function(i,name){jQuery.fn[name]=function(data,fn){return arguments.length>0?this.on(name,null,data,fn):this.trigger(name)}});jQuery.fn.extend({hover:function(fnOver,fnOut){return this.mouseenter(fnOver).mouseleave(fnOut||fnOver)}});var location=window.location;var nonce=jQuery.now();var rquery=/\?/;var rvalidtokens=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;jQuery.parseJSON=function(data){if(window.JSON&&window.JSON.parse){return window.JSON.parse(data+"")}var requireNonComma,depth=null,str=jQuery.trim(data+"");return str&&!jQuery.trim(str.replace(rvalidtokens,function(token,comma,open,close){if(requireNonComma&&comma){depth=0}if(depth===0){return token}requireNonComma=open||comma;depth+=!close-!open;return""}))?Function("return "+str)():jQuery.error("Invalid JSON: "+data)};jQuery.parseXML=function(data){var xml,tmp;if(!data||typeof data!=="string"){return null}try{if(window.DOMParser){tmp=new window.DOMParser;xml=tmp.parseFromString(data,"text/xml")}else{xml=new window.ActiveXObject("Microsoft.XMLDOM");xml.async="false";xml.loadXML(data)}}catch(e){xml=undefined}if(!xml||!xml.documentElement||xml.getElementsByTagName("parsererror").length){jQuery.error("Invalid XML: "+data)}return xml};var rhash=/#.*$/,rts=/([?&])_=[^&]*/,rheaders=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,rlocalProtocol=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,rnoContent=/^(?:GET|HEAD)$/,rprotocol=/^\/\//,rurl=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,prefilters={},transports={},allTypes="*/".concat("*"),ajaxLocation=location.href,ajaxLocParts=rurl.exec(ajaxLocation.toLowerCase())||[];function addToPrefiltersOrTransports(structure){return function(dataTypeExpression,func){if(typeof dataTypeExpression!=="string"){func=dataTypeExpression;dataTypeExpression="*"}var dataType,i=0,dataTypes=dataTypeExpression.toLowerCase().match(rnotwhite)||[];if(jQuery.isFunction(func)){while(dataType=dataTypes[i++]){if(dataType.charAt(0)==="+"){dataType=dataType.slice(1)||"*";(structure[dataType]=structure[dataType]||[]).unshift(func)}else{(structure[dataType]=structure[dataType]||[]).push(func)}}}}}function inspectPrefiltersOrTransports(structure,options,originalOptions,jqXHR){var inspected={},seekingTransport=structure===transports;function inspect(dataType){var selected;inspected[dataType]=true;jQuery.each(structure[dataType]||[],function(_,prefilterOrFactory){var dataTypeOrTransport=prefilterOrFactory(options,originalOptions,jqXHR);if(typeof dataTypeOrTransport==="string"&&!seekingTransport&&!inspected[dataTypeOrTransport]){options.dataTypes.unshift(dataTypeOrTransport);inspect(dataTypeOrTransport);return false}else if(seekingTransport){return!(selected=dataTypeOrTransport)}});return selected}return inspect(options.dataTypes[0])||!inspected["*"]&&inspect("*")}function ajaxExtend(target,src){var deep,key,flatOptions=jQuery.ajaxSettings.flatOptions||{};for(key in src){if(src[key]!==undefined){(flatOptions[key]?target:deep||(deep={}))[key]=src[key]}}if(deep){jQuery.extend(true,target,deep)}return target}function ajaxHandleResponses(s,jqXHR,responses){var firstDataType,ct,finalDataType,type,contents=s.contents,dataTypes=s.dataTypes;while(dataTypes[0]==="*"){dataTypes.shift();if(ct===undefined){ct=s.mimeType||jqXHR.getResponseHeader("Content-Type")}}if(ct){for(type in contents){if(contents[type]&&contents[type].test(ct)){dataTypes.unshift(type);break}}}if(dataTypes[0]in responses){finalDataType=dataTypes[0]}else{for(type in responses){if(!dataTypes[0]||s.converters[type+" "+dataTypes[0]]){finalDataType=type;break}if(!firstDataType){firstDataType=type}}finalDataType=finalDataType||firstDataType}if(finalDataType){if(finalDataType!==dataTypes[0]){dataTypes.unshift(finalDataType)}return responses[finalDataType]}}function ajaxConvert(s,response,jqXHR,isSuccess){var conv2,current,conv,tmp,prev,converters={},dataTypes=s.dataTypes.slice();if(dataTypes[1]){for(conv in s.converters){converters[conv.toLowerCase()]=s.converters[conv]}}current=dataTypes.shift();while(current){if(s.responseFields[current]){jqXHR[s.responseFields[current]]=response}if(!prev&&isSuccess&&s.dataFilter){response=s.dataFilter(response,s.dataType)}prev=current;current=dataTypes.shift();if(current){if(current==="*"){current=prev}else if(prev!=="*"&&prev!==current){conv=converters[prev+" "+current]||converters["* "+current];if(!conv){for(conv2 in converters){tmp=conv2.split(" ");if(tmp[1]===current){conv=converters[prev+" "+tmp[0]]||converters["* "+tmp[0]];if(conv){if(conv===true){conv=converters[conv2]}else if(converters[conv2]!==true){current=tmp[0];dataTypes.unshift(tmp[1])}break}}}}if(conv!==true){if(conv&&s["throws"]){response=conv(response)}else{try{response=conv(response)}catch(e){return{state:"parsererror",error:conv?e:"No conversion from "+prev+" to "+current}}}}}}}return{state:"success",data:response}}jQuery.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:ajaxLocation,type:"GET",isLocal:rlocalProtocol.test(ajaxLocParts[1]),global:true,processData:true,async:true,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":allTypes,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":true,"text json":jQuery.parseJSON,"text xml":jQuery.parseXML},flatOptions:{url:true,context:true}},ajaxSetup:function(target,settings){return settings?ajaxExtend(ajaxExtend(target,jQuery.ajaxSettings),settings):ajaxExtend(jQuery.ajaxSettings,target)},ajaxPrefilter:addToPrefiltersOrTransports(prefilters),ajaxTransport:addToPrefiltersOrTransports(transports),ajax:function(url,options){if(typeof url==="object"){options=url;url=undefined}options=options||{};var parts,i,cacheURL,responseHeadersString,timeoutTimer,fireGlobals,transport,responseHeaders,s=jQuery.ajaxSetup({},options),callbackContext=s.context||s,globalEventContext=s.context&&(callbackContext.nodeType||callbackContext.jquery)?jQuery(callbackContext):jQuery.event,deferred=jQuery.Deferred(),completeDeferred=jQuery.Callbacks("once memory"),statusCode=s.statusCode||{},requestHeaders={},requestHeadersNames={},state=0,strAbort="canceled",jqXHR={readyState:0,getResponseHeader:function(key){var match;if(state===2){if(!responseHeaders){responseHeaders={};while(match=rheaders.exec(responseHeadersString)){responseHeaders[match[1].toLowerCase()]=match[2]}}match=responseHeaders[key.toLowerCase()]}return match==null?null:match},getAllResponseHeaders:function(){return state===2?responseHeadersString:null},setRequestHeader:function(name,value){var lname=name.toLowerCase();if(!state){name=requestHeadersNames[lname]=requestHeadersNames[lname]||name;requestHeaders[name]=value}return this},overrideMimeType:function(type){if(!state){s.mimeType=type}return this},statusCode:function(map){var code;if(map){if(state<2){for(code in map){statusCode[code]=[statusCode[code],map[code]]}}else{jqXHR.always(map[jqXHR.status])}}return this},abort:function(statusText){var finalText=statusText||strAbort;if(transport){transport.abort(finalText)}done(0,finalText);return this}};deferred.promise(jqXHR).complete=completeDeferred.add;jqXHR.success=jqXHR.done;jqXHR.error=jqXHR.fail;s.url=((url||s.url||ajaxLocation)+"").replace(rhash,"").replace(rprotocol,ajaxLocParts[1]+"//");s.type=options.method||options.type||s.method||s.type;s.dataTypes=jQuery.trim(s.dataType||"*").toLowerCase().match(rnotwhite)||[""];if(s.crossDomain==null){parts=rurl.exec(s.url.toLowerCase());s.crossDomain=!!(parts&&(parts[1]!==ajaxLocParts[1]||parts[2]!==ajaxLocParts[2]||(parts[3]||(parts[1]==="http:"?"80":"443"))!==(ajaxLocParts[3]||(ajaxLocParts[1]==="http:"?"80":"443"))))}if(s.data&&s.processData&&typeof s.data!=="string"){s.data=jQuery.param(s.data,s.traditional)}inspectPrefiltersOrTransports(prefilters,s,options,jqXHR);if(state===2){return jqXHR}fireGlobals=jQuery.event&&s.global;if(fireGlobals&&jQuery.active++===0){jQuery.event.trigger("ajaxStart")}s.type=s.type.toUpperCase();s.hasContent=!rnoContent.test(s.type);cacheURL=s.url;if(!s.hasContent){if(s.data){cacheURL=s.url+=(rquery.test(cacheURL)?"&":"?")+s.data;delete s.data}if(s.cache===false){s.url=rts.test(cacheURL)?cacheURL.replace(rts,"$1_="+nonce++):cacheURL+(rquery.test(cacheURL)?"&":"?")+"_="+nonce++}}if(s.ifModified){if(jQuery.lastModified[cacheURL]){jqXHR.setRequestHeader("If-Modified-Since",jQuery.lastModified[cacheURL])}if(jQuery.etag[cacheURL]){jqXHR.setRequestHeader("If-None-Match",jQuery.etag[cacheURL])}}if(s.data&&s.hasContent&&s.contentType!==false||options.contentType){jqXHR.setRequestHeader("Content-Type",s.contentType)}jqXHR.setRequestHeader("Accept",s.dataTypes[0]&&s.accepts[s.dataTypes[0]]?s.accepts[s.dataTypes[0]]+(s.dataTypes[0]!=="*"?", "+allTypes+"; q=0.01":""):s.accepts["*"]);for(i in s.headers){jqXHR.setRequestHeader(i,s.headers[i])}if(s.beforeSend&&(s.beforeSend.call(callbackContext,jqXHR,s)===false||state===2)){return jqXHR.abort()}strAbort="abort";for(i in{success:1,error:1,complete:1}){jqXHR[i](s[i])}transport=inspectPrefiltersOrTransports(transports,s,options,jqXHR);if(!transport){done(-1,"No Transport")}else{jqXHR.readyState=1;if(fireGlobals){globalEventContext.trigger("ajaxSend",[jqXHR,s])}if(state===2){return jqXHR}if(s.async&&s.timeout>0){timeoutTimer=window.setTimeout(function(){jqXHR.abort("timeout")},s.timeout)}try{state=1;transport.send(requestHeaders,done)}catch(e){if(state<2){done(-1,e)}else{throw e}}}function done(status,nativeStatusText,responses,headers){var isSuccess,success,error,response,modified,statusText=nativeStatusText;if(state===2){return}state=2;if(timeoutTimer){window.clearTimeout(timeoutTimer)}transport=undefined;responseHeadersString=headers||"";jqXHR.readyState=status>0?4:0;isSuccess=status>=200&&status<300||status===304;if(responses){response=ajaxHandleResponses(s,jqXHR,responses)}response=ajaxConvert(s,response,jqXHR,isSuccess);if(isSuccess){if(s.ifModified){modified=jqXHR.getResponseHeader("Last-Modified");if(modified){jQuery.lastModified[cacheURL]=modified}modified=jqXHR.getResponseHeader("etag");if(modified){jQuery.etag[cacheURL]=modified}}if(status===204||s.type==="HEAD"){statusText="nocontent"}else if(status===304){statusText="notmodified"}else{statusText=response.state;success=response.data;error=response.error;isSuccess=!error}}else{error=statusText;if(status||!statusText){statusText="error";if(status<0){status=0}}}jqXHR.status=status;jqXHR.statusText=(nativeStatusText||statusText)+"";if(isSuccess){deferred.resolveWith(callbackContext,[success,statusText,jqXHR])}else{deferred.rejectWith(callbackContext,[jqXHR,statusText,error])}jqXHR.statusCode(statusCode);statusCode=undefined;if(fireGlobals){globalEventContext.trigger(isSuccess?"ajaxSuccess":"ajaxError",[jqXHR,s,isSuccess?success:error])}completeDeferred.fireWith(callbackContext,[jqXHR,statusText]);if(fireGlobals){globalEventContext.trigger("ajaxComplete",[jqXHR,s]);if(!--jQuery.active){jQuery.event.trigger("ajaxStop")}}}return jqXHR},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json")},getScript:function(url,callback){return jQuery.get(url,undefined,callback,"script")}});jQuery.each(["get","post"],function(i,method){jQuery[method]=function(url,data,callback,type){if(jQuery.isFunction(data)){type=type||callback;callback=data;data=undefined}return jQuery.ajax(jQuery.extend({url:url,type:method,dataType:type,data:data,success:callback},jQuery.isPlainObject(url)&&url))}});jQuery._evalUrl=function(url){return jQuery.ajax({url:url,type:"GET",dataType:"script",cache:true,async:false,global:false,"throws":true})};jQuery.fn.extend({wrapAll:function(html){if(jQuery.isFunction(html)){return this.each(function(i){jQuery(this).wrapAll(html.call(this,i))})}if(this[0]){var wrap=jQuery(html,this[0].ownerDocument).eq(0).clone(true);if(this[0].parentNode){wrap.insertBefore(this[0])}wrap.map(function(){var elem=this;while(elem.firstChild&&elem.firstChild.nodeType===1){elem=elem.firstChild}return elem}).append(this)}return this},wrapInner:function(html){if(jQuery.isFunction(html)){return this.each(function(i){jQuery(this).wrapInner(html.call(this,i))})}return this.each(function(){var self=jQuery(this),contents=self.contents();if(contents.length){contents.wrapAll(html)}else{self.append(html)}})},wrap:function(html){var isFunction=jQuery.isFunction(html);return this.each(function(i){jQuery(this).wrapAll(isFunction?html.call(this,i):html)})},unwrap:function(){return this.parent().each(function(){if(!jQuery.nodeName(this,"body")){jQuery(this).replaceWith(this.childNodes)}}).end()}});function getDisplay(elem){return elem.style&&elem.style.display||jQuery.css(elem,"display")}function filterHidden(elem){while(elem&&elem.nodeType===1){if(getDisplay(elem)==="none"||elem.type==="hidden"){return true}elem=elem.parentNode}return false}jQuery.expr.filters.hidden=function(elem){return support.reliableHiddenOffsets()?elem.offsetWidth<=0&&elem.offsetHeight<=0&&!elem.getClientRects().length:filterHidden(elem)};jQuery.expr.filters.visible=function(elem){return!jQuery.expr.filters.hidden(elem)};var r20=/%20/g,rbracket=/\[\]$/,rCRLF=/\r?\n/g,rsubmitterTypes=/^(?:submit|button|image|reset|file)$/i,rsubmittable=/^(?:input|select|textarea|keygen)/i;function buildParams(prefix,obj,traditional,add){var name;if(jQuery.isArray(obj)){jQuery.each(obj,function(i,v){if(traditional||rbracket.test(prefix)){add(prefix,v)}else{buildParams(prefix+"["+(typeof v==="object"&&v!=null?i:"")+"]",v,traditional,add)}})}else if(!traditional&&jQuery.type(obj)==="object"){for(name in obj){buildParams(prefix+"["+name+"]",obj[name],traditional,add)}}else{add(prefix,obj)}}jQuery.param=function(a,traditional){var prefix,s=[],add=function(key,value){value=jQuery.isFunction(value)?value():value==null?"":value;s[s.length]=encodeURIComponent(key)+"="+encodeURIComponent(value)};if(traditional===undefined){traditional=jQuery.ajaxSettings&&jQuery.ajaxSettings.traditional}if(jQuery.isArray(a)||a.jquery&&!jQuery.isPlainObject(a)){jQuery.each(a,function(){add(this.name,this.value)})}else{for(prefix in a){buildParams(prefix,a[prefix],traditional,add)}}return s.join("&").replace(r20,"+")};jQuery.fn.extend({serialize:function(){return jQuery.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var elements=jQuery.prop(this,"elements");return elements?jQuery.makeArray(elements):this}).filter(function(){var type=this.type;return this.name&&!jQuery(this).is(":disabled")&&rsubmittable.test(this.nodeName)&&!rsubmitterTypes.test(type)&&(this.checked||!rcheckableType.test(type))}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:jQuery.isArray(val)?jQuery.map(val,function(val){return{name:elem.name,value:val.replace(rCRLF,"\r\n")}}):{name:elem.name,value:val.replace(rCRLF,"\r\n")}}).get()}});jQuery.ajaxSettings.xhr=window.ActiveXObject!==undefined?function(){if(this.isLocal){return createActiveXHR()}if(document.documentMode>8){return createStandardXHR()}return/^(get|post|head|put|delete|options)$/i.test(this.type)&&createStandardXHR()||createActiveXHR()}:createStandardXHR;var xhrId=0,xhrCallbacks={},xhrSupported=jQuery.ajaxSettings.xhr();if(window.attachEvent){window.attachEvent("onunload",function(){for(var key in xhrCallbacks){xhrCallbacks[key](undefined,true)}})}support.cors=!!xhrSupported&&"withCredentials"in xhrSupported; -xhrSupported=support.ajax=!!xhrSupported;if(xhrSupported){jQuery.ajaxTransport(function(options){if(!options.crossDomain||support.cors){var callback;return{send:function(headers,complete){var i,xhr=options.xhr(),id=++xhrId;xhr.open(options.type,options.url,options.async,options.username,options.password);if(options.xhrFields){for(i in options.xhrFields){xhr[i]=options.xhrFields[i]}}if(options.mimeType&&xhr.overrideMimeType){xhr.overrideMimeType(options.mimeType)}if(!options.crossDomain&&!headers["X-Requested-With"]){headers["X-Requested-With"]="XMLHttpRequest"}for(i in headers){if(headers[i]!==undefined){xhr.setRequestHeader(i,headers[i]+"")}}xhr.send(options.hasContent&&options.data||null);callback=function(_,isAbort){var status,statusText,responses;if(callback&&(isAbort||xhr.readyState===4)){delete xhrCallbacks[id];callback=undefined;xhr.onreadystatechange=jQuery.noop;if(isAbort){if(xhr.readyState!==4){xhr.abort()}}else{responses={};status=xhr.status;if(typeof xhr.responseText==="string"){responses.text=xhr.responseText}try{statusText=xhr.statusText}catch(e){statusText=""}if(!status&&options.isLocal&&!options.crossDomain){status=responses.text?200:404}else if(status===1223){status=204}}}if(responses){complete(status,statusText,responses,xhr.getAllResponseHeaders())}};if(!options.async){callback()}else if(xhr.readyState===4){window.setTimeout(callback)}else{xhr.onreadystatechange=xhrCallbacks[id]=callback}},abort:function(){if(callback){callback(undefined,true)}}}}})}function createStandardXHR(){try{return new window.XMLHttpRequest}catch(e){}}function createActiveXHR(){try{return new window.ActiveXObject("Microsoft.XMLHTTP")}catch(e){}}jQuery.ajaxPrefilter(function(s){if(s.crossDomain){s.contents.script=false}});jQuery.ajaxSetup({accepts:{script:"text/javascript, application/javascript, "+"application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(text){jQuery.globalEval(text);return text}}});jQuery.ajaxPrefilter("script",function(s){if(s.cache===undefined){s.cache=false}if(s.crossDomain){s.type="GET";s.global=false}});jQuery.ajaxTransport("script",function(s){if(s.crossDomain){var script,head=document.head||jQuery("head")[0]||document.documentElement;return{send:function(_,callback){script=document.createElement("script");script.async=true;if(s.scriptCharset){script.charset=s.scriptCharset}script.src=s.url;script.onload=script.onreadystatechange=function(_,isAbort){if(isAbort||!script.readyState||/loaded|complete/.test(script.readyState)){script.onload=script.onreadystatechange=null;if(script.parentNode){script.parentNode.removeChild(script)}script=null;if(!isAbort){callback(200,"success")}}};head.insertBefore(script,head.firstChild)},abort:function(){if(script){script.onload(undefined,true)}}}}});var oldCallbacks=[],rjsonp=/(=)\?(?=&|$)|\?\?/;jQuery.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var callback=oldCallbacks.pop()||jQuery.expando+"_"+nonce++;this[callback]=true;return callback}});jQuery.ajaxPrefilter("json jsonp",function(s,originalSettings,jqXHR){var callbackName,overwritten,responseContainer,jsonProp=s.jsonp!==false&&(rjsonp.test(s.url)?"url":typeof s.data==="string"&&(s.contentType||"").indexOf("application/x-www-form-urlencoded")===0&&rjsonp.test(s.data)&&"data");if(jsonProp||s.dataTypes[0]==="jsonp"){callbackName=s.jsonpCallback=jQuery.isFunction(s.jsonpCallback)?s.jsonpCallback():s.jsonpCallback;if(jsonProp){s[jsonProp]=s[jsonProp].replace(rjsonp,"$1"+callbackName)}else if(s.jsonp!==false){s.url+=(rquery.test(s.url)?"&":"?")+s.jsonp+"="+callbackName}s.converters["script json"]=function(){if(!responseContainer){jQuery.error(callbackName+" was not called")}return responseContainer[0]};s.dataTypes[0]="json";overwritten=window[callbackName];window[callbackName]=function(){responseContainer=arguments};jqXHR.always(function(){if(overwritten===undefined){jQuery(window).removeProp(callbackName)}else{window[callbackName]=overwritten}if(s[callbackName]){s.jsonpCallback=originalSettings.jsonpCallback;oldCallbacks.push(callbackName)}if(responseContainer&&jQuery.isFunction(overwritten)){overwritten(responseContainer[0])}responseContainer=overwritten=undefined});return"script"}});support.createHTMLDocument=function(){if(!document.implementation.createHTMLDocument){return false}var doc=document.implementation.createHTMLDocument("");doc.body.innerHTML="
";return doc.body.childNodes.length===2}();jQuery.parseHTML=function(data,context,keepScripts){if(!data||typeof data!=="string"){return null}if(typeof context==="boolean"){keepScripts=context;context=false}context=context||(support.createHTMLDocument?document.implementation.createHTMLDocument(""):document);var parsed=rsingleTag.exec(data),scripts=!keepScripts&&[];if(parsed){return[context.createElement(parsed[1])]}parsed=buildFragment([data],context,scripts);if(scripts&&scripts.length){jQuery(scripts).remove()}return jQuery.merge([],parsed.childNodes)};var _load=jQuery.fn.load;jQuery.fn.load=function(url,params,callback){if(typeof url!=="string"&&_load){return _load.apply(this,arguments)}var selector,type,response,self=this,off=url.indexOf(" ");if(off>-1){selector=jQuery.trim(url.slice(off,url.length));url=url.slice(0,off)}if(jQuery.isFunction(params)){callback=params;params=undefined}else if(params&&typeof params==="object"){type="POST"}if(self.length>0){jQuery.ajax({url:url,type:type||"GET",dataType:"html",data:params}).done(function(responseText){response=arguments;self.html(selector?jQuery("
").append(jQuery.parseHTML(responseText)).find(selector):responseText)}).always(callback&&function(jqXHR,status){self.each(function(){callback.apply(self,response||[jqXHR.responseText,status,jqXHR])})})}return this};jQuery.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(i,type){jQuery.fn[type]=function(fn){return this.on(type,fn)}});jQuery.expr.filters.animated=function(elem){return jQuery.grep(jQuery.timers,function(fn){return elem===fn.elem}).length};function getWindow(elem){return jQuery.isWindow(elem)?elem:elem.nodeType===9?elem.defaultView||elem.parentWindow:false}jQuery.offset={setOffset:function(elem,options,i){var curPosition,curLeft,curCSSTop,curTop,curOffset,curCSSLeft,calculatePosition,position=jQuery.css(elem,"position"),curElem=jQuery(elem),props={};if(position==="static"){elem.style.position="relative"}curOffset=curElem.offset();curCSSTop=jQuery.css(elem,"top");curCSSLeft=jQuery.css(elem,"left");calculatePosition=(position==="absolute"||position==="fixed")&&jQuery.inArray("auto",[curCSSTop,curCSSLeft])>-1;if(calculatePosition){curPosition=curElem.position();curTop=curPosition.top;curLeft=curPosition.left}else{curTop=parseFloat(curCSSTop)||0;curLeft=parseFloat(curCSSLeft)||0}if(jQuery.isFunction(options)){options=options.call(elem,i,jQuery.extend({},curOffset))}if(options.top!=null){props.top=options.top-curOffset.top+curTop}if(options.left!=null){props.left=options.left-curOffset.left+curLeft}if("using"in options){options.using.call(elem,props)}else{curElem.css(props)}}};jQuery.fn.extend({offset:function(options){if(arguments.length){return options===undefined?this:this.each(function(i){jQuery.offset.setOffset(this,options,i)})}var docElem,win,box={top:0,left:0},elem=this[0],doc=elem&&elem.ownerDocument;if(!doc){return}docElem=doc.documentElement;if(!jQuery.contains(docElem,elem)){return box}if(typeof elem.getBoundingClientRect!=="undefined"){box=elem.getBoundingClientRect()}win=getWindow(doc);return{top:box.top+(win.pageYOffset||docElem.scrollTop)-(docElem.clientTop||0),left:box.left+(win.pageXOffset||docElem.scrollLeft)-(docElem.clientLeft||0)}},position:function(){if(!this[0]){return}var offsetParent,offset,parentOffset={top:0,left:0},elem=this[0];if(jQuery.css(elem,"position")==="fixed"){offset=elem.getBoundingClientRect()}else{offsetParent=this.offsetParent();offset=this.offset();if(!jQuery.nodeName(offsetParent[0],"html")){parentOffset=offsetParent.offset()}parentOffset.top+=jQuery.css(offsetParent[0],"borderTopWidth",true);parentOffset.left+=jQuery.css(offsetParent[0],"borderLeftWidth",true)}return{top:offset.top-parentOffset.top-jQuery.css(elem,"marginTop",true),left:offset.left-parentOffset.left-jQuery.css(elem,"marginLeft",true)}},offsetParent:function(){return this.map(function(){var offsetParent=this.offsetParent;while(offsetParent&&(!jQuery.nodeName(offsetParent,"html")&&jQuery.css(offsetParent,"position")==="static")){offsetParent=offsetParent.offsetParent}return offsetParent||documentElement})}});jQuery.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(method,prop){var top=/Y/.test(prop);jQuery.fn[method]=function(val){return access(this,function(elem,method,val){var win=getWindow(elem);if(val===undefined){return win?prop in win?win[prop]:win.document.documentElement[method]:elem[method]}if(win){win.scrollTo(!top?val:jQuery(win).scrollLeft(),top?val:jQuery(win).scrollTop())}else{elem[method]=val}},method,val,arguments.length,null)}});jQuery.each(["top","left"],function(i,prop){jQuery.cssHooks[prop]=addGetHookIf(support.pixelPosition,function(elem,computed){if(computed){computed=curCSS(elem,prop);return rnumnonpx.test(computed)?jQuery(elem).position()[prop]+"px":computed}})});jQuery.each({Height:"height",Width:"width"},function(name,type){jQuery.each({padding:"inner"+name,content:type,"":"outer"+name},function(defaultExtra,funcName){jQuery.fn[funcName]=function(margin,value){var chainable=arguments.length&&(defaultExtra||typeof margin!=="boolean"),extra=defaultExtra||(margin===true||value===true?"margin":"border");return access(this,function(elem,type,value){var doc;if(jQuery.isWindow(elem)){return elem.document.documentElement["client"+name]}if(elem.nodeType===9){doc=elem.documentElement;return Math.max(elem.body["scroll"+name],doc["scroll"+name],elem.body["offset"+name],doc["offset"+name],doc["client"+name])}return value===undefined?jQuery.css(elem,type,extra):jQuery.style(elem,type,value,extra)},type,chainable?margin:undefined,chainable,null)}})});jQuery.fn.extend({bind:function(types,data,fn){return this.on(types,null,data,fn)},unbind:function(types,fn){return this.off(types,null,fn)},delegate:function(selector,types,data,fn){return this.on(types,selector,data,fn)},undelegate:function(selector,types,fn){return arguments.length===1?this.off(selector,"**"):this.off(types,selector||"**",fn)}});jQuery.fn.size=function(){return this.length};jQuery.fn.andSelf=jQuery.fn.addBack;if(typeof define==="function"&&define.amd){define("jquery",[],function(){return jQuery})}var _jQuery=window.jQuery,_$=window.$;jQuery.noConflict=function(deep){if(window.$===jQuery){window.$=_$}if(deep&&window.jQuery===jQuery){window.jQuery=_jQuery}return jQuery};if(!noGlobal){window.jQuery=window.$=jQuery}return jQuery})},{}],8:[function(require,module,exports){(function(){module.exports={xpath:require("./xpath"),Range:require("./range")}}).call(this)},{"./range":9,"./xpath":11}],9:[function(require,module,exports){(function(){var $,Range,Util,xpath,__hasProp={}.hasOwnProperty,__extends=function(child,parent){for(var key in parent){if(__hasProp.call(parent,key))child[key]=parent[key]}function ctor(){this.constructor=child}ctor.prototype=parent.prototype;child.prototype=new ctor;child.__super__=parent.prototype;return child};xpath=require("./xpath");Util=require("./util");$=require("jquery");Range={};Range.sniff=function(r){if(r.commonAncestorContainer!=null){return new Range.BrowserRange(r)}else if(typeof r.start==="string"){return new Range.SerializedRange(r)}else if(r.start&&typeof r.start==="object"){return new Range.NormalizedRange(r)}else{console.error("Could not sniff range type");return false}};Range.RangeError=function(_super){__extends(RangeError,_super);function RangeError(type,message,parent){this.type=type;this.message=message;this.parent=parent!=null?parent:null;RangeError.__super__.constructor.call(this,this.message)}return RangeError}(Error);Range.BrowserRange=function(){function BrowserRange(obj){this.commonAncestorContainer=obj.commonAncestorContainer;this.startContainer=obj.startContainer;this.startOffset=obj.startOffset;this.endContainer=obj.endContainer;this.endOffset=obj.endOffset}BrowserRange.prototype.normalize=function(root){var nr,r;if(this.tainted){console.error("You may only call normalize() once on a BrowserRange!");return false}else{this.tainted=true}r={};this._normalizeStart(r);this._normalizeEnd(r);nr={};if(r.startOffset>0){if(r.start.nodeValue.length>r.startOffset){nr.start=r.start.splitText(r.startOffset)}else{nr.start=r.start.nextSibling}}else{nr.start=r.start}if(r.start===r.end){if(nr.start.nodeValue.length>r.endOffset-r.startOffset){nr.start.splitText(r.endOffset-r.startOffset)}nr.end=nr.start}else{if(r.end.nodeValue.length>r.endOffset){r.end.splitText(r.endOffset)}nr.end=r.end}nr.commonAncestor=this.commonAncestorContainer;while(nr.commonAncestor.nodeType!==Util.NodeTypes.ELEMENT_NODE){nr.commonAncestor=nr.commonAncestor.parentNode}return new Range.NormalizedRange(nr)};BrowserRange.prototype._normalizeStart=function(r){if(this.startContainer.nodeType===Util.NodeTypes.ELEMENT_NODE){r.start=Util.getFirstTextNodeNotBefore(this.startContainer.childNodes[this.startOffset]);return r.startOffset=0}else{r.start=this.startContainer;return r.startOffset=this.startOffset}};BrowserRange.prototype._normalizeEnd=function(r){var n,node;if(this.endContainer.nodeType===Util.NodeTypes.ELEMENT_NODE){node=this.endContainer.childNodes[this.endOffset];if(node!=null){n=node;while(n!=null&&n.nodeType!==Util.NodeTypes.TEXT_NODE){n=n.firstChild}if(n!=null){r.end=n;r.endOffset=0}}if(r.end==null){if(this.endOffset){node=this.endContainer.childNodes[this.endOffset-1]}else{node=this.endContainer.previousSibling}r.end=Util.getLastTextNodeUpTo(node);return r.endOffset=r.end.nodeValue.length}}else{r.end=this.endContainer;return r.endOffset=this.endOffset}};BrowserRange.prototype.serialize=function(root,ignoreSelector){return this.normalize(root).serialize(root,ignoreSelector)};return BrowserRange}();Range.NormalizedRange=function(){function NormalizedRange(obj){this.commonAncestor=obj.commonAncestor;this.start=obj.start;this.end=obj.end}NormalizedRange.prototype.normalize=function(root){return this};NormalizedRange.prototype.limit=function(bounds){var nodes,parent,startParents,_i,_len,_ref;nodes=$.grep(this.textNodes(),function(node){return node.parentNode===bounds||$.contains(bounds,node.parentNode)});if(!nodes.length){return null}this.start=nodes[0];this.end=nodes[nodes.length-1];startParents=$(this.start).parents();_ref=$(this.end).parents();for(_i=0,_len=_ref.length;_i<_len;_i++){parent=_ref[_i];if(startParents.index(parent)!==-1){this.commonAncestor=parent;break}}return this};NormalizedRange.prototype.serialize=function(root,ignoreSelector){var end,serialization,start;serialization=function(node,isEnd){var n,nodes,offset,origParent,path,textNodes,_i,_len;if(ignoreSelector){origParent=$(node).parents(":not("+ignoreSelector+")").eq(0)}else{origParent=$(node).parent()}path=xpath.fromNode(origParent,root)[0];textNodes=Util.getTextNodes(origParent);nodes=textNodes.slice(0,textNodes.index(node));offset=0;for(_i=0,_len=nodes.length;_i<_len;_i++){n=nodes[_i];offset+=n.nodeValue.length}if(isEnd){return[path,offset+node.nodeValue.length]}else{return[path,offset]}};start=serialization(this.start);end=serialization(this.end,true);return new Range.SerializedRange({start:start[0],end:end[0],startOffset:start[1],endOffset:end[1]})};NormalizedRange.prototype.text=function(){var node;return function(){var _i,_len,_ref,_results;_ref=this.textNodes();_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++){node=_ref[_i];_results.push(node.nodeValue)}return _results}.call(this).join("")};NormalizedRange.prototype.textNodes=function(){var end,start,textNodes,_ref;textNodes=Util.getTextNodes($(this.commonAncestor));_ref=[textNodes.index(this.start),textNodes.index(this.end)],start=_ref[0],end=_ref[1];return $.makeArray(textNodes.slice(start,+end+1||9e9))};return NormalizedRange}();Range.SerializedRange=function(){function SerializedRange(obj){this.start=obj.start;this.startOffset=obj.startOffset;this.end=obj.end;this.endOffset=obj.endOffset}SerializedRange.prototype.normalize=function(root){var contains,e,length,node,p,range,targetOffset,tn,_i,_j,_len,_len1,_ref,_ref1;range={};_ref=["start","end"];for(_i=0,_len=_ref.length;_i<_len;_i++){p=_ref[_i];try{node=xpath.toNode(this[p],root)}catch(_error){e=_error;throw new Range.RangeError(p,"Error while finding "+p+" node: "+this[p]+": "+e,e)}if(!node){throw new Range.RangeError(p,"Couldn't find "+p+" node: "+this[p])}length=0;targetOffset=this[p+"Offset"];if(p==="end"){targetOffset-=1}_ref1=Util.getTextNodes($(node));for(_j=0,_len1=_ref1.length;_j<_len1;_j++){tn=_ref1[_j];if(length+tn.nodeValue.length>targetOffset){range[p+"Container"]=tn;range[p+"Offset"]=this[p+"Offset"]-length;break}else{length+=tn.nodeValue.length}}if(range[p+"Offset"]==null){throw new Range.RangeError(""+p+"offset","Couldn't find offset "+this[p+"Offset"]+" in element "+this[p])}}contains=document.compareDocumentPosition!=null?function(a,b){return a.compareDocumentPosition(b)&Node.DOCUMENT_POSITION_CONTAINED_BY}:function(a,b){return a.contains(b)};$(range.startContainer).parents().each(function(){var endContainer;if(range.endContainer.nodeType===Util.NodeTypes.TEXT_NODE){endContainer=range.endContainer.parentNode}else{endContainer=range.endContainer}if(contains(this,endContainer)){range.commonAncestorContainer=this;return false}});return new Range.BrowserRange(range).normalize(root)};SerializedRange.prototype.serialize=function(root,ignoreSelector){return this.normalize(root).serialize(root,ignoreSelector)};SerializedRange.prototype.toObject=function(){return{start:this.start,startOffset:this.startOffset,end:this.end,endOffset:this.endOffset}};return SerializedRange}();module.exports=Range}).call(this)},{"./util":10,"./xpath":11,jquery:12}],10:[function(require,module,exports){(function(){var $,Util;$=require("jquery");Util={};Util.NodeTypes={ELEMENT_NODE:1,ATTRIBUTE_NODE:2,TEXT_NODE:3,CDATA_SECTION_NODE:4,ENTITY_REFERENCE_NODE:5,ENTITY_NODE:6,PROCESSING_INSTRUCTION_NODE:7,COMMENT_NODE:8,DOCUMENT_NODE:9,DOCUMENT_TYPE_NODE:10,DOCUMENT_FRAGMENT_NODE:11,NOTATION_NODE:12};Util.getFirstTextNodeNotBefore=function(n){var result;switch(n.nodeType){case Util.NodeTypes.TEXT_NODE:return n;case Util.NodeTypes.ELEMENT_NODE:if(n.firstChild!=null){result=Util.getFirstTextNodeNotBefore(n.firstChild);if(result!=null){return result}}break}n=n.nextSibling;if(n!=null){return Util.getFirstTextNodeNotBefore(n)}else{return null}};Util.getLastTextNodeUpTo=function(n){var result;switch(n.nodeType){case Util.NodeTypes.TEXT_NODE:return n;case Util.NodeTypes.ELEMENT_NODE:if(n.lastChild!=null){result=Util.getLastTextNodeUpTo(n.lastChild);if(result!=null){return result}}break}n=n.previousSibling;if(n!=null){return Util.getLastTextNodeUpTo(n)}else{return null}};Util.getTextNodes=function(jq){var getTextNodes;getTextNodes=function(node){var nodes;if(node&&node.nodeType!==Util.NodeTypes.TEXT_NODE){nodes=[];if(node.nodeType!==Util.NodeTypes.COMMENT_NODE){node=node.lastChild;while(node){nodes.push(getTextNodes(node));node=node.previousSibling}}return nodes.reverse()}else{return node}};return jq.map(function(){return Util.flatten(getTextNodes(this))})};Util.getGlobal=function(){return function(){return this}()};Util.contains=function(parent,child){var node;node=child;while(node!=null){if(node===parent){return true}node=node.parentNode}return false};Util.flatten=function(array){var flatten;flatten=function(ary){var el,flat,_i,_len;flat=[];for(_i=0,_len=ary.length;_i<_len;_i++){el=ary[_i];flat=flat.concat(el&&$.isArray(el)?flatten(el):el)}return flat};return flatten(array)};module.exports=Util}).call(this)},{jquery:12}],11:[function(require,module,exports){(function(){var $,Util,evaluateXPath,findChild,fromNode,getNodeName,getNodePosition,simpleXPathJQuery,simpleXPathPure,toNode;$=require("jquery");Util=require("./util");evaluateXPath=function(xp,root,nsResolver){var exception,idx,name,node,step,steps,_i,_len,_ref;if(root==null){root=document}if(nsResolver==null){nsResolver=null}try{return document.evaluate("."+xp,root,nsResolver,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue}catch(_error){exception=_error;console.log("XPath evaluation failed.");console.log("Trying fallback...");steps=xp.substring(1).split("/");node=root;for(_i=0,_len=steps.length;_i<_len;_i++){step=steps[_i];_ref=step.split("["),name=_ref[0],idx=_ref[1];idx=idx!=null?parseInt((idx!=null?idx.split("]"):void 0)[0]):1;node=findChild(node,name.toLowerCase(),idx)}return node}};simpleXPathJQuery=function($el,relativeRoot){var jq;jq=$el.map(function(){var elem,idx,path,tagName;path="";elem=this;while((elem!=null?elem.nodeType:void 0)===Util.NodeTypes.ELEMENT_NODE&&elem!==relativeRoot){tagName=elem.tagName.replace(":","\\:");idx=$(elem.parentNode).children(tagName).index(elem)+1;idx="["+idx+"]";path="/"+elem.tagName.toLowerCase()+idx+path;elem=elem.parentNode}return path});return jq.get()};simpleXPathPure=function($el,relativeRoot){var getPathSegment,getPathTo,jq,rootNode;getPathSegment=function(node){var name,pos;name=getNodeName(node);pos=getNodePosition(node);return""+name+"["+pos+"]"};rootNode=relativeRoot;getPathTo=function(node){var xpath;xpath="";while(node!==rootNode){if(node==null){throw new Error("Called getPathTo on a node which was not a descendant of @rootNode. "+rootNode)}xpath=getPathSegment(node)+"/"+xpath;node=node.parentNode}xpath="/"+xpath;xpath=xpath.replace(/\/$/,"");return xpath};jq=$el.map(function(){var path;path=getPathTo(this);return path});return jq.get()};findChild=function(node,type,index){var child,children,found,name,_i,_len;if(!node.hasChildNodes()){throw new Error("XPath error: node has no children!")}children=node.childNodes;found=0;for(_i=0,_len=children.length;_i<_len;_i++){child=children[_i];name=getNodeName(child);if(name===type){found+=1;if(found===index){return child}}}throw new Error("XPath error: wanted child not found.")};getNodeName=function(node){var nodeName;nodeName=node.nodeName.toLowerCase();switch(nodeName){case"#text":return"text()";case"#comment":return"comment()";case"#cdata-section":return"cdata-section()";default:return nodeName}};getNodePosition=function(node){var pos,tmp;pos=0;tmp=node;while(tmp){if(tmp.nodeName===node.nodeName){pos+=1}tmp=tmp.previousSibling}return pos};fromNode=function($el,relativeRoot){var exception,result;try{result=simpleXPathJQuery($el,relativeRoot)}catch(_error){exception=_error;console.log("jQuery-based XPath construction failed! Falling back to manual.");result=simpleXPathPure($el,relativeRoot)}return result};toNode=function(path,root){var customResolver,namespace,node,segment;if(root==null){root=document}if(!$.isXMLDoc(document.documentElement)){return evaluateXPath(path,root)}else{customResolver=document.createNSResolver(document.ownerDocument===null?document.documentElement:document.ownerDocument.documentElement);node=evaluateXPath(path,root,customResolver);if(!node){path=function(){var _i,_len,_ref,_results;_ref=path.split("/");_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++){segment=_ref[_i];if(segment&&segment.indexOf(":")===-1){_results.push(segment.replace(/^([a-z]+)/,"xhtml:$1"))}else{_results.push(segment)}}return _results}().join("/");namespace=document.lookupNamespaceURI(null);customResolver=function(ns){if(ns==="xhtml"){return namespace}else{return document.documentElement.getAttribute("xmlns:"+ns)}};node=evaluateXPath(path,root,customResolver)}return node}};module.exports={fromNode:fromNode,toNode:toNode}}).call(this)},{"./util":10,jquery:12}],12:[function(require,module,exports){(function(global,factory){if(typeof module==="object"&&typeof module.exports==="object"){module.exports=global.document?factory(global,true):function(w){if(!w.document){throw new Error("jQuery requires a window with a document")}return factory(w)}}else{factory(global)}})(typeof window!=="undefined"?window:this,function(window,noGlobal){var deletedIds=[];var slice=deletedIds.slice;var concat=deletedIds.concat;var push=deletedIds.push;var indexOf=deletedIds.indexOf;var class2type={};var toString=class2type.toString;var hasOwn=class2type.hasOwnProperty;var support={};var version="1.11.3",jQuery=function(selector,context){return new jQuery.fn.init(selector,context)},rtrim=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,rmsPrefix=/^-ms-/,rdashAlpha=/-([\da-z])/gi,fcamelCase=function(all,letter){return letter.toUpperCase()};jQuery.fn=jQuery.prototype={jquery:version,constructor:jQuery,selector:"",length:0,toArray:function(){return slice.call(this)},get:function(num){return num!=null?num<0?this[num+this.length]:this[num]:slice.call(this)},pushStack:function(elems){var ret=jQuery.merge(this.constructor(),elems);ret.prevObject=this;ret.context=this.context;return ret},each:function(callback,args){return jQuery.each(this,callback,args)},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem)}))},slice:function(){return this.pushStack(slice.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(i){var len=this.length,j=+i+(i<0?len:0);return this.pushStack(j>=0&&j=0},isEmptyObject:function(obj){var name;for(name in obj){return false}return true},isPlainObject:function(obj){var key;if(!obj||jQuery.type(obj)!=="object"||obj.nodeType||jQuery.isWindow(obj)){return false}try{if(obj.constructor&&!hasOwn.call(obj,"constructor")&&!hasOwn.call(obj.constructor.prototype,"isPrototypeOf")){return false}}catch(e){return false}if(support.ownLast){for(key in obj){return hasOwn.call(obj,key)}}for(key in obj){}return key===undefined||hasOwn.call(obj,key)},type:function(obj){if(obj==null){return obj+""}return typeof obj==="object"||typeof obj==="function"?class2type[toString.call(obj)]||"object":typeof obj},globalEval:function(data){if(data&&jQuery.trim(data)){(window.execScript||function(data){window["eval"].call(window,data)})(data)}},camelCase:function(string){return string.replace(rmsPrefix,"ms-").replace(rdashAlpha,fcamelCase)},nodeName:function(elem,name){return elem.nodeName&&elem.nodeName.toLowerCase()===name.toLowerCase()},each:function(obj,callback,args){var value,i=0,length=obj.length,isArray=isArraylike(obj);if(args){if(isArray){for(;i0&&length-1 in obj}var Sizzle=function(window){var i,support,Expr,getText,isXML,tokenize,compile,select,outermostContext,sortInput,hasDuplicate,setDocument,document,docElem,documentIsHTML,rbuggyQSA,rbuggyMatches,matches,contains,expando="sizzle"+1*new Date,preferredDoc=window.document,dirruns=0,done=0,classCache=createCache(),tokenCache=createCache(),compilerCache=createCache(),sortOrder=function(a,b){if(a===b){hasDuplicate=true}return 0},MAX_NEGATIVE=1<<31,hasOwn={}.hasOwnProperty,arr=[],pop=arr.pop,push_native=arr.push,push=arr.push,slice=arr.slice,indexOf=function(list,elem){var i=0,len=list.length;for(;i+~]|"+whitespace+")"+whitespace+"*"),rattributeQuotes=new RegExp("="+whitespace+"*([^\\]'\"]*?)"+whitespace+"*\\]","g"),rpseudo=new RegExp(pseudos),ridentifier=new RegExp("^"+identifier+"$"),matchExpr={ID:new RegExp("^#("+characterEncoding+")"),CLASS:new RegExp("^\\.("+characterEncoding+")"),TAG:new RegExp("^("+characterEncoding.replace("w","w*")+")"),ATTR:new RegExp("^"+attributes),PSEUDO:new RegExp("^"+pseudos), -CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+whitespace+"*(even|odd|(([+-]|)(\\d*)n|)"+whitespace+"*(?:([+-]|)"+whitespace+"*(\\d+)|))"+whitespace+"*\\)|)","i"),bool:new RegExp("^(?:"+booleans+")$","i"),needsContext:new RegExp("^"+whitespace+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+whitespace+"*((?:-\\d)?\\d*)"+whitespace+"*\\)|)(?=[^-]|$)","i")},rinputs=/^(?:input|select|textarea|button)$/i,rheader=/^h\d$/i,rnative=/^[^{]+\{\s*\[native \w/,rquickExpr=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,rsibling=/[+~]/,rescape=/'|\\/g,runescape=new RegExp("\\\\([\\da-f]{1,6}"+whitespace+"?|("+whitespace+")|.)","ig"),funescape=function(_,escaped,escapedWhitespace){var high="0x"+escaped-65536;return high!==high||escapedWhitespace?escaped:high<0?String.fromCharCode(high+65536):String.fromCharCode(high>>10|55296,high&1023|56320)},unloadHandler=function(){setDocument()};try{push.apply(arr=slice.call(preferredDoc.childNodes),preferredDoc.childNodes);arr[preferredDoc.childNodes.length].nodeType}catch(e){push={apply:arr.length?function(target,els){push_native.apply(target,slice.call(els))}:function(target,els){var j=target.length,i=0;while(target[j++]=els[i++]){}target.length=j-1}}}function Sizzle(selector,context,results,seed){var match,elem,m,nodeType,i,groups,old,nid,newContext,newSelector;if((context?context.ownerDocument||context:preferredDoc)!==document){setDocument(context)}context=context||document;results=results||[];nodeType=context.nodeType;if(typeof selector!=="string"||!selector||nodeType!==1&&nodeType!==9&&nodeType!==11){return results}if(!seed&&documentIsHTML){if(nodeType!==11&&(match=rquickExpr.exec(selector))){if(m=match[1]){if(nodeType===9){elem=context.getElementById(m);if(elem&&elem.parentNode){if(elem.id===m){results.push(elem);return results}}else{return results}}else{if(context.ownerDocument&&(elem=context.ownerDocument.getElementById(m))&&contains(context,elem)&&elem.id===m){results.push(elem);return results}}}else if(match[2]){push.apply(results,context.getElementsByTagName(selector));return results}else if((m=match[3])&&support.getElementsByClassName){push.apply(results,context.getElementsByClassName(m));return results}}if(support.qsa&&(!rbuggyQSA||!rbuggyQSA.test(selector))){nid=old=expando;newContext=context;newSelector=nodeType!==1&&selector;if(nodeType===1&&context.nodeName.toLowerCase()!=="object"){groups=tokenize(selector);if(old=context.getAttribute("id")){nid=old.replace(rescape,"\\$&")}else{context.setAttribute("id",nid)}nid="[id='"+nid+"'] ";i=groups.length;while(i--){groups[i]=nid+toSelector(groups[i])}newContext=rsibling.test(selector)&&testContext(context.parentNode)||context;newSelector=groups.join(",")}if(newSelector){try{push.apply(results,newContext.querySelectorAll(newSelector));return results}catch(qsaError){}finally{if(!old){context.removeAttribute("id")}}}}}return select(selector.replace(rtrim,"$1"),context,results,seed)}function createCache(){var keys=[];function cache(key,value){if(keys.push(key+" ")>Expr.cacheLength){delete cache[keys.shift()]}return cache[key+" "]=value}return cache}function markFunction(fn){fn[expando]=true;return fn}function assert(fn){var div=document.createElement("div");try{return!!fn(div)}catch(e){return false}finally{if(div.parentNode){div.parentNode.removeChild(div)}div=null}}function addHandle(attrs,handler){var arr=attrs.split("|"),i=attrs.length;while(i--){Expr.attrHandle[arr[i]]=handler}}function siblingCheck(a,b){var cur=b&&a,diff=cur&&a.nodeType===1&&b.nodeType===1&&(~b.sourceIndex||MAX_NEGATIVE)-(~a.sourceIndex||MAX_NEGATIVE);if(diff){return diff}if(cur){while(cur=cur.nextSibling){if(cur===b){return-1}}}return a?1:-1}function createInputPseudo(type){return function(elem){var name=elem.nodeName.toLowerCase();return name==="input"&&elem.type===type}}function createButtonPseudo(type){return function(elem){var name=elem.nodeName.toLowerCase();return(name==="input"||name==="button")&&elem.type===type}}function createPositionalPseudo(fn){return markFunction(function(argument){argument=+argument;return markFunction(function(seed,matches){var j,matchIndexes=fn([],seed.length,argument),i=matchIndexes.length;while(i--){if(seed[j=matchIndexes[i]]){seed[j]=!(matches[j]=seed[j])}}})})}function testContext(context){return context&&typeof context.getElementsByTagName!=="undefined"&&context}support=Sizzle.support={};isXML=Sizzle.isXML=function(elem){var documentElement=elem&&(elem.ownerDocument||elem).documentElement;return documentElement?documentElement.nodeName!=="HTML":false};setDocument=Sizzle.setDocument=function(node){var hasCompare,parent,doc=node?node.ownerDocument||node:preferredDoc;if(doc===document||doc.nodeType!==9||!doc.documentElement){return document}document=doc;docElem=doc.documentElement;parent=doc.defaultView;if(parent&&parent!==parent.top){if(parent.addEventListener){parent.addEventListener("unload",unloadHandler,false)}else if(parent.attachEvent){parent.attachEvent("onunload",unloadHandler)}}documentIsHTML=!isXML(doc);support.attributes=assert(function(div){div.className="i";return!div.getAttribute("className")});support.getElementsByTagName=assert(function(div){div.appendChild(doc.createComment(""));return!div.getElementsByTagName("*").length});support.getElementsByClassName=rnative.test(doc.getElementsByClassName);support.getById=assert(function(div){docElem.appendChild(div).id=expando;return!doc.getElementsByName||!doc.getElementsByName(expando).length});if(support.getById){Expr.find["ID"]=function(id,context){if(typeof context.getElementById!=="undefined"&&documentIsHTML){var m=context.getElementById(id);return m&&m.parentNode?[m]:[]}};Expr.filter["ID"]=function(id){var attrId=id.replace(runescape,funescape);return function(elem){return elem.getAttribute("id")===attrId}}}else{delete Expr.find["ID"];Expr.filter["ID"]=function(id){var attrId=id.replace(runescape,funescape);return function(elem){var node=typeof elem.getAttributeNode!=="undefined"&&elem.getAttributeNode("id");return node&&node.value===attrId}}}Expr.find["TAG"]=support.getElementsByTagName?function(tag,context){if(typeof context.getElementsByTagName!=="undefined"){return context.getElementsByTagName(tag)}else if(support.qsa){return context.querySelectorAll(tag)}}:function(tag,context){var elem,tmp=[],i=0,results=context.getElementsByTagName(tag);if(tag==="*"){while(elem=results[i++]){if(elem.nodeType===1){tmp.push(elem)}}return tmp}return results};Expr.find["CLASS"]=support.getElementsByClassName&&function(className,context){if(documentIsHTML){return context.getElementsByClassName(className)}};rbuggyMatches=[];rbuggyQSA=[];if(support.qsa=rnative.test(doc.querySelectorAll)){assert(function(div){docElem.appendChild(div).innerHTML=""+"";if(div.querySelectorAll("[msallowcapture^='']").length){rbuggyQSA.push("[*^$]="+whitespace+"*(?:''|\"\")")}if(!div.querySelectorAll("[selected]").length){rbuggyQSA.push("\\["+whitespace+"*(?:value|"+booleans+")")}if(!div.querySelectorAll("[id~="+expando+"-]").length){rbuggyQSA.push("~=")}if(!div.querySelectorAll(":checked").length){rbuggyQSA.push(":checked")}if(!div.querySelectorAll("a#"+expando+"+*").length){rbuggyQSA.push(".#.+[+~]")}});assert(function(div){var input=doc.createElement("input");input.setAttribute("type","hidden");div.appendChild(input).setAttribute("name","D");if(div.querySelectorAll("[name=d]").length){rbuggyQSA.push("name"+whitespace+"*[*^$|!~]?=")}if(!div.querySelectorAll(":enabled").length){rbuggyQSA.push(":enabled",":disabled")}div.querySelectorAll("*,:x");rbuggyQSA.push(",.*:")})}if(support.matchesSelector=rnative.test(matches=docElem.matches||docElem.webkitMatchesSelector||docElem.mozMatchesSelector||docElem.oMatchesSelector||docElem.msMatchesSelector)){assert(function(div){support.disconnectedMatch=matches.call(div,"div");matches.call(div,"[s!='']:x");rbuggyMatches.push("!=",pseudos)})}rbuggyQSA=rbuggyQSA.length&&new RegExp(rbuggyQSA.join("|"));rbuggyMatches=rbuggyMatches.length&&new RegExp(rbuggyMatches.join("|"));hasCompare=rnative.test(docElem.compareDocumentPosition);contains=hasCompare||rnative.test(docElem.contains)?function(a,b){var adown=a.nodeType===9?a.documentElement:a,bup=b&&b.parentNode;return a===bup||!!(bup&&bup.nodeType===1&&(adown.contains?adown.contains(bup):a.compareDocumentPosition&&a.compareDocumentPosition(bup)&16))}:function(a,b){if(b){while(b=b.parentNode){if(b===a){return true}}}return false};sortOrder=hasCompare?function(a,b){if(a===b){hasDuplicate=true;return 0}var compare=!a.compareDocumentPosition-!b.compareDocumentPosition;if(compare){return compare}compare=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1;if(compare&1||!support.sortDetached&&b.compareDocumentPosition(a)===compare){if(a===doc||a.ownerDocument===preferredDoc&&contains(preferredDoc,a)){return-1}if(b===doc||b.ownerDocument===preferredDoc&&contains(preferredDoc,b)){return 1}return sortInput?indexOf(sortInput,a)-indexOf(sortInput,b):0}return compare&4?-1:1}:function(a,b){if(a===b){hasDuplicate=true;return 0}var cur,i=0,aup=a.parentNode,bup=b.parentNode,ap=[a],bp=[b];if(!aup||!bup){return a===doc?-1:b===doc?1:aup?-1:bup?1:sortInput?indexOf(sortInput,a)-indexOf(sortInput,b):0}else if(aup===bup){return siblingCheck(a,b)}cur=a;while(cur=cur.parentNode){ap.unshift(cur)}cur=b;while(cur=cur.parentNode){bp.unshift(cur)}while(ap[i]===bp[i]){i++}return i?siblingCheck(ap[i],bp[i]):ap[i]===preferredDoc?-1:bp[i]===preferredDoc?1:0};return doc};Sizzle.matches=function(expr,elements){return Sizzle(expr,null,null,elements)};Sizzle.matchesSelector=function(elem,expr){if((elem.ownerDocument||elem)!==document){setDocument(elem)}expr=expr.replace(rattributeQuotes,"='$1']");if(support.matchesSelector&&documentIsHTML&&(!rbuggyMatches||!rbuggyMatches.test(expr))&&(!rbuggyQSA||!rbuggyQSA.test(expr))){try{var ret=matches.call(elem,expr);if(ret||support.disconnectedMatch||elem.document&&elem.document.nodeType!==11){return ret}}catch(e){}}return Sizzle(expr,document,null,[elem]).length>0};Sizzle.contains=function(context,elem){if((context.ownerDocument||context)!==document){setDocument(context)}return contains(context,elem)};Sizzle.attr=function(elem,name){if((elem.ownerDocument||elem)!==document){setDocument(elem)}var fn=Expr.attrHandle[name.toLowerCase()],val=fn&&hasOwn.call(Expr.attrHandle,name.toLowerCase())?fn(elem,name,!documentIsHTML):undefined;return val!==undefined?val:support.attributes||!documentIsHTML?elem.getAttribute(name):(val=elem.getAttributeNode(name))&&val.specified?val.value:null};Sizzle.error=function(msg){throw new Error("Syntax error, unrecognized expression: "+msg)};Sizzle.uniqueSort=function(results){var elem,duplicates=[],j=0,i=0;hasDuplicate=!support.detectDuplicates;sortInput=!support.sortStable&&results.slice(0);results.sort(sortOrder);if(hasDuplicate){while(elem=results[i++]){if(elem===results[i]){j=duplicates.push(i)}}while(j--){results.splice(duplicates[j],1)}}sortInput=null;return results};getText=Sizzle.getText=function(elem){var node,ret="",i=0,nodeType=elem.nodeType;if(!nodeType){while(node=elem[i++]){ret+=getText(node)}}else if(nodeType===1||nodeType===9||nodeType===11){if(typeof elem.textContent==="string"){return elem.textContent}else{for(elem=elem.firstChild;elem;elem=elem.nextSibling){ret+=getText(elem)}}}else if(nodeType===3||nodeType===4){return elem.nodeValue}return ret};Expr=Sizzle.selectors={cacheLength:50,createPseudo:markFunction,match:matchExpr,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:true}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:true},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(match){match[1]=match[1].replace(runescape,funescape);match[3]=(match[3]||match[4]||match[5]||"").replace(runescape,funescape);if(match[2]==="~="){match[3]=" "+match[3]+" "}return match.slice(0,4)},CHILD:function(match){match[1]=match[1].toLowerCase();if(match[1].slice(0,3)==="nth"){if(!match[3]){Sizzle.error(match[0])}match[4]=+(match[4]?match[5]+(match[6]||1):2*(match[3]==="even"||match[3]==="odd"));match[5]=+(match[7]+match[8]||match[3]==="odd")}else if(match[3]){Sizzle.error(match[0])}return match},PSEUDO:function(match){var excess,unquoted=!match[6]&&match[2];if(matchExpr["CHILD"].test(match[0])){return null}if(match[3]){match[2]=match[4]||match[5]||""}else if(unquoted&&rpseudo.test(unquoted)&&(excess=tokenize(unquoted,true))&&(excess=unquoted.indexOf(")",unquoted.length-excess)-unquoted.length)){match[0]=match[0].slice(0,excess);match[2]=unquoted.slice(0,excess)}return match.slice(0,3)}},filter:{TAG:function(nodeNameSelector){var nodeName=nodeNameSelector.replace(runescape,funescape).toLowerCase();return nodeNameSelector==="*"?function(){return true}:function(elem){return elem.nodeName&&elem.nodeName.toLowerCase()===nodeName}},CLASS:function(className){var pattern=classCache[className+" "];return pattern||(pattern=new RegExp("(^|"+whitespace+")"+className+"("+whitespace+"|$)"))&&classCache(className,function(elem){return pattern.test(typeof elem.className==="string"&&elem.className||typeof elem.getAttribute!=="undefined"&&elem.getAttribute("class")||"")})},ATTR:function(name,operator,check){return function(elem){var result=Sizzle.attr(elem,name);if(result==null){return operator==="!="}if(!operator){return true}result+="";return operator==="="?result===check:operator==="!="?result!==check:operator==="^="?check&&result.indexOf(check)===0:operator==="*="?check&&result.indexOf(check)>-1:operator==="$="?check&&result.slice(-check.length)===check:operator==="~="?(" "+result.replace(rwhitespace," ")+" ").indexOf(check)>-1:operator==="|="?result===check||result.slice(0,check.length+1)===check+"-":false}},CHILD:function(type,what,argument,first,last){var simple=type.slice(0,3)!=="nth",forward=type.slice(-4)!=="last",ofType=what==="of-type";return first===1&&last===0?function(elem){return!!elem.parentNode}:function(elem,context,xml){var cache,outerCache,node,diff,nodeIndex,start,dir=simple!==forward?"nextSibling":"previousSibling",parent=elem.parentNode,name=ofType&&elem.nodeName.toLowerCase(),useCache=!xml&&!ofType;if(parent){if(simple){while(dir){node=elem;while(node=node[dir]){if(ofType?node.nodeName.toLowerCase()===name:node.nodeType===1){return false}}start=dir=type==="only"&&!start&&"nextSibling"}return true}start=[forward?parent.firstChild:parent.lastChild];if(forward&&useCache){outerCache=parent[expando]||(parent[expando]={});cache=outerCache[type]||[];nodeIndex=cache[0]===dirruns&&cache[1];diff=cache[0]===dirruns&&cache[2];node=nodeIndex&&parent.childNodes[nodeIndex];while(node=++nodeIndex&&node&&node[dir]||(diff=nodeIndex=0)||start.pop()){if(node.nodeType===1&&++diff&&node===elem){outerCache[type]=[dirruns,nodeIndex,diff];break}}}else if(useCache&&(cache=(elem[expando]||(elem[expando]={}))[type])&&cache[0]===dirruns){diff=cache[1]}else{while(node=++nodeIndex&&node&&node[dir]||(diff=nodeIndex=0)||start.pop()){if((ofType?node.nodeName.toLowerCase()===name:node.nodeType===1)&&++diff){if(useCache){(node[expando]||(node[expando]={}))[type]=[dirruns,diff]}if(node===elem){break}}}}diff-=last;return diff===first||diff%first===0&&diff/first>=0}}},PSEUDO:function(pseudo,argument){var args,fn=Expr.pseudos[pseudo]||Expr.setFilters[pseudo.toLowerCase()]||Sizzle.error("unsupported pseudo: "+pseudo);if(fn[expando]){return fn(argument)}if(fn.length>1){args=[pseudo,pseudo,"",argument];return Expr.setFilters.hasOwnProperty(pseudo.toLowerCase())?markFunction(function(seed,matches){var idx,matched=fn(seed,argument),i=matched.length;while(i--){idx=indexOf(seed,matched[i]);seed[idx]=!(matches[idx]=matched[i])}}):function(elem){return fn(elem,0,args)}}return fn}},pseudos:{not:markFunction(function(selector){var input=[],results=[],matcher=compile(selector.replace(rtrim,"$1"));return matcher[expando]?markFunction(function(seed,matches,context,xml){var elem,unmatched=matcher(seed,null,xml,[]),i=seed.length;while(i--){if(elem=unmatched[i]){seed[i]=!(matches[i]=elem)}}}):function(elem,context,xml){input[0]=elem;matcher(input,null,xml,results);input[0]=null;return!results.pop()}}),has:markFunction(function(selector){return function(elem){return Sizzle(selector,elem).length>0}}),contains:markFunction(function(text){text=text.replace(runescape,funescape);return function(elem){return(elem.textContent||elem.innerText||getText(elem)).indexOf(text)>-1}}),lang:markFunction(function(lang){if(!ridentifier.test(lang||"")){Sizzle.error("unsupported lang: "+lang)}lang=lang.replace(runescape,funescape).toLowerCase();return function(elem){var elemLang;do{if(elemLang=documentIsHTML?elem.lang:elem.getAttribute("xml:lang")||elem.getAttribute("lang")){elemLang=elemLang.toLowerCase();return elemLang===lang||elemLang.indexOf(lang+"-")===0}}while((elem=elem.parentNode)&&elem.nodeType===1);return false}}),target:function(elem){var hash=window.location&&window.location.hash;return hash&&hash.slice(1)===elem.id},root:function(elem){return elem===docElem},focus:function(elem){return elem===document.activeElement&&(!document.hasFocus||document.hasFocus())&&!!(elem.type||elem.href||~elem.tabIndex)},enabled:function(elem){return elem.disabled===false},disabled:function(elem){return elem.disabled===true},checked:function(elem){var nodeName=elem.nodeName.toLowerCase();return nodeName==="input"&&!!elem.checked||nodeName==="option"&&!!elem.selected},selected:function(elem){if(elem.parentNode){elem.parentNode.selectedIndex}return elem.selected===true},empty:function(elem){for(elem=elem.firstChild;elem;elem=elem.nextSibling){if(elem.nodeType<6){return false}}return true},parent:function(elem){return!Expr.pseudos["empty"](elem)},header:function(elem){return rheader.test(elem.nodeName)},input:function(elem){return rinputs.test(elem.nodeName)},button:function(elem){var name=elem.nodeName.toLowerCase();return name==="input"&&elem.type==="button"||name==="button"},text:function(elem){var attr;return elem.nodeName.toLowerCase()==="input"&&elem.type==="text"&&((attr=elem.getAttribute("type"))==null||attr.toLowerCase()==="text")},first:createPositionalPseudo(function(){return[0]}),last:createPositionalPseudo(function(matchIndexes,length){return[length-1]}),eq:createPositionalPseudo(function(matchIndexes,length,argument){return[argument<0?argument+length:argument]}),even:createPositionalPseudo(function(matchIndexes,length){var i=0;for(;i=0;){matchIndexes.push(i)}return matchIndexes}),gt:createPositionalPseudo(function(matchIndexes,length,argument){var i=argument<0?argument+length:argument;for(;++i1?function(elem,context,xml){var i=matchers.length;while(i--){if(!matchers[i](elem,context,xml)){return false}}return true}:matchers[0]}function multipleContexts(selector,contexts,results){var i=0,len=contexts.length;for(;i-1){seed[temp]=!(results[temp]=elem)}}}}else{matcherOut=condense(matcherOut===results?matcherOut.splice(preexisting,matcherOut.length):matcherOut);if(postFinder){postFinder(null,results,matcherOut,xml)}else{push.apply(results,matcherOut)}}})}function matcherFromTokens(tokens){var checkContext,matcher,j,len=tokens.length,leadingRelative=Expr.relative[tokens[0].type],implicitRelative=leadingRelative||Expr.relative[" "],i=leadingRelative?1:0,matchContext=addCombinator(function(elem){return elem===checkContext},implicitRelative,true),matchAnyContext=addCombinator(function(elem){return indexOf(checkContext,elem)>-1},implicitRelative,true),matchers=[function(elem,context,xml){var ret=!leadingRelative&&(xml||context!==outermostContext)||((checkContext=context).nodeType?matchContext(elem,context,xml):matchAnyContext(elem,context,xml));checkContext=null;return ret}];for(;i1&&elementMatcher(matchers),i>1&&toSelector(tokens.slice(0,i-1).concat({value:tokens[i-2].type===" "?"*":""})).replace(rtrim,"$1"),matcher,i0,byElement=elementMatchers.length>0,superMatcher=function(seed,context,xml,results,outermost){var elem,j,matcher,matchedCount=0,i="0",unmatched=seed&&[],setMatched=[],contextBackup=outermostContext,elems=seed||byElement&&Expr.find["TAG"]("*",outermost),dirrunsUnique=dirruns+=contextBackup==null?1:Math.random()||.1,len=elems.length;if(outermost){outermostContext=context!==document&&context}for(;i!==len&&(elem=elems[i])!=null;i++){if(byElement&&elem){j=0;while(matcher=elementMatchers[j++]){if(matcher(elem,context,xml)){results.push(elem);break}}if(outermost){dirruns=dirrunsUnique}}if(bySet){if(elem=!matcher&&elem){matchedCount--}if(seed){unmatched.push(elem)}}}matchedCount+=i;if(bySet&&i!==matchedCount){j=0;while(matcher=setMatchers[j++]){matcher(unmatched,setMatched,context,xml)}if(seed){if(matchedCount>0){while(i--){if(!(unmatched[i]||setMatched[i])){setMatched[i]=pop.call(results)}}}setMatched=condense(setMatched)}push.apply(results,setMatched);if(outermost&&!seed&&setMatched.length>0&&matchedCount+setMatchers.length>1){Sizzle.uniqueSort(results)}}if(outermost){dirruns=dirrunsUnique;outermostContext=contextBackup}return unmatched};return bySet?markFunction(superMatcher):superMatcher}compile=Sizzle.compile=function(selector,match){var i,setMatchers=[],elementMatchers=[],cached=compilerCache[selector+" "];if(!cached){if(!match){match=tokenize(selector)}i=match.length;while(i--){cached=matcherFromTokens(match[i]);if(cached[expando]){setMatchers.push(cached)}else{elementMatchers.push(cached)}}cached=compilerCache(selector,matcherFromGroupMatchers(elementMatchers,setMatchers));cached.selector=selector}return cached};select=Sizzle.select=function(selector,context,results,seed){var i,tokens,token,type,find,compiled=typeof selector==="function"&&selector,match=!seed&&tokenize(selector=compiled.selector||selector);results=results||[];if(match.length===1){tokens=match[0]=match[0].slice(0);if(tokens.length>2&&(token=tokens[0]).type==="ID"&&support.getById&&context.nodeType===9&&documentIsHTML&&Expr.relative[tokens[1].type]){context=(Expr.find["ID"](token.matches[0].replace(runescape,funescape),context)||[])[0];if(!context){return results}else if(compiled){context=context.parentNode}selector=selector.slice(tokens.shift().value.length)}i=matchExpr["needsContext"].test(selector)?0:tokens.length;while(i--){token=tokens[i];if(Expr.relative[type=token.type]){break}if(find=Expr.find[type]){if(seed=find(token.matches[0].replace(runescape,funescape),rsibling.test(tokens[0].type)&&testContext(context.parentNode)||context)){tokens.splice(i,1);selector=seed.length&&toSelector(tokens);if(!selector){push.apply(results,seed);return results}break}}}}(compiled||compile(selector,match))(seed,context,!documentIsHTML,results,rsibling.test(selector)&&testContext(context.parentNode)||context);return results};support.sortStable=expando.split("").sort(sortOrder).join("")===expando;support.detectDuplicates=!!hasDuplicate;setDocument();support.sortDetached=assert(function(div1){return div1.compareDocumentPosition(document.createElement("div"))&1});if(!assert(function(div){div.innerHTML="";return div.firstChild.getAttribute("href")==="#"})){addHandle("type|href|height|width",function(elem,name,isXML){if(!isXML){return elem.getAttribute(name,name.toLowerCase()==="type"?1:2)}})}if(!support.attributes||!assert(function(div){div.innerHTML="";div.firstChild.setAttribute("value","");return div.firstChild.getAttribute("value")===""})){addHandle("value",function(elem,name,isXML){if(!isXML&&elem.nodeName.toLowerCase()==="input"){return elem.defaultValue}})}if(!assert(function(div){return div.getAttribute("disabled")==null})){addHandle(booleans,function(elem,name,isXML){var val;if(!isXML){return elem[name]===true?name.toLowerCase():(val=elem.getAttributeNode(name))&&val.specified?val.value:null}})}return Sizzle}(window);jQuery.find=Sizzle;jQuery.expr=Sizzle.selectors;jQuery.expr[":"]=jQuery.expr.pseudos;jQuery.unique=Sizzle.uniqueSort;jQuery.text=Sizzle.getText;jQuery.isXMLDoc=Sizzle.isXML;jQuery.contains=Sizzle.contains;var rneedsContext=jQuery.expr.match.needsContext;var rsingleTag=/^<(\w+)\s*\/?>(?:<\/\1>|)$/;var risSimple=/^.[^:#\[\.,]*$/;function winnow(elements,qualifier,not){if(jQuery.isFunction(qualifier)){return jQuery.grep(elements,function(elem,i){return!!qualifier.call(elem,i,elem)!==not})}if(qualifier.nodeType){return jQuery.grep(elements,function(elem){return elem===qualifier!==not})}if(typeof qualifier==="string"){if(risSimple.test(qualifier)){return jQuery.filter(qualifier,elements,not)}qualifier=jQuery.filter(qualifier,elements)}return jQuery.grep(elements,function(elem){return jQuery.inArray(elem,qualifier)>=0!==not})}jQuery.filter=function(expr,elems,not){var elem=elems[0];if(not){expr=":not("+expr+")"}return elems.length===1&&elem.nodeType===1?jQuery.find.matchesSelector(elem,expr)?[elem]:[]:jQuery.find.matches(expr,jQuery.grep(elems,function(elem){return elem.nodeType===1}))};jQuery.fn.extend({find:function(selector){var i,ret=[],self=this,len=self.length;if(typeof selector!=="string"){return this.pushStack(jQuery(selector).filter(function(){for(i=0;i1?jQuery.unique(ret):ret);ret.selector=this.selector?this.selector+" "+selector:selector;return ret},filter:function(selector){return this.pushStack(winnow(this,selector||[],false))},not:function(selector){return this.pushStack(winnow(this,selector||[],true))},is:function(selector){return!!winnow(this,typeof selector==="string"&&rneedsContext.test(selector)?jQuery(selector):selector||[],false).length}});var rootjQuery,document=window.document,rquickExpr=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,init=jQuery.fn.init=function(selector,context){var match,elem;if(!selector){return this}if(typeof selector==="string"){if(selector.charAt(0)==="<"&&selector.charAt(selector.length-1)===">"&&selector.length>=3){match=[null,selector,null]}else{match=rquickExpr.exec(selector)}if(match&&(match[1]||!context)){if(match[1]){context=context instanceof jQuery?context[0]:context;jQuery.merge(this,jQuery.parseHTML(match[1],context&&context.nodeType?context.ownerDocument||context:document,true));if(rsingleTag.test(match[1])&&jQuery.isPlainObject(context)){for(match in context){if(jQuery.isFunction(this[match])){this[match](context[match])}else{this.attr(match,context[match])}}}return this}else{elem=document.getElementById(match[2]);if(elem&&elem.parentNode){if(elem.id!==match[2]){return rootjQuery.find(selector)}this.length=1;this[0]=elem}this.context=document;this.selector=selector;return this}}else if(!context||context.jquery){return(context||rootjQuery).find(selector)}else{return this.constructor(context).find(selector)}}else if(selector.nodeType){this.context=this[0]=selector;this.length=1;return this}else if(jQuery.isFunction(selector)){return typeof rootjQuery.ready!=="undefined"?rootjQuery.ready(selector):selector(jQuery)}if(selector.selector!==undefined){this.selector=selector.selector;this.context=selector.context}return jQuery.makeArray(selector,this)};init.prototype=jQuery.fn;rootjQuery=jQuery(document);var rparentsprev=/^(?:parents|prev(?:Until|All))/,guaranteedUnique={children:true,contents:true,next:true,prev:true};jQuery.extend({dir:function(elem,dir,until){var matched=[],cur=elem[dir];while(cur&&cur.nodeType!==9&&(until===undefined||cur.nodeType!==1||!jQuery(cur).is(until))){if(cur.nodeType===1){matched.push(cur)}cur=cur[dir]}return matched},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType===1&&n!==elem){r.push(n); -}}return r}});jQuery.fn.extend({has:function(target){var i,targets=jQuery(target,this),len=targets.length;return this.filter(function(){for(i=0;i-1:cur.nodeType===1&&jQuery.find.matchesSelector(cur,selectors))){matched.push(cur);break}}}return this.pushStack(matched.length>1?jQuery.unique(matched):matched)},index:function(elem){if(!elem){return this[0]&&this[0].parentNode?this.first().prevAll().length:-1}if(typeof elem==="string"){return jQuery.inArray(this[0],jQuery(elem))}return jQuery.inArray(elem.jquery?elem[0]:elem,this)},add:function(selector,context){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),jQuery(selector,context))))},addBack:function(selector){return this.add(selector==null?this.prevObject:this.prevObject.filter(selector))}});function sibling(cur,dir){do{cur=cur[dir]}while(cur&&cur.nodeType!==1);return cur}jQuery.each({parent:function(elem){var parent=elem.parentNode;return parent&&parent.nodeType!==11?parent:null},parents:function(elem){return jQuery.dir(elem,"parentNode")},parentsUntil:function(elem,i,until){return jQuery.dir(elem,"parentNode",until)},next:function(elem){return sibling(elem,"nextSibling")},prev:function(elem){return sibling(elem,"previousSibling")},nextAll:function(elem){return jQuery.dir(elem,"nextSibling")},prevAll:function(elem){return jQuery.dir(elem,"previousSibling")},nextUntil:function(elem,i,until){return jQuery.dir(elem,"nextSibling",until)},prevUntil:function(elem,i,until){return jQuery.dir(elem,"previousSibling",until)},siblings:function(elem){return jQuery.sibling((elem.parentNode||{}).firstChild,elem)},children:function(elem){return jQuery.sibling(elem.firstChild)},contents:function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.merge([],elem.childNodes)}},function(name,fn){jQuery.fn[name]=function(until,selector){var ret=jQuery.map(this,fn,until);if(name.slice(-5)!=="Until"){selector=until}if(selector&&typeof selector==="string"){ret=jQuery.filter(selector,ret)}if(this.length>1){if(!guaranteedUnique[name]){ret=jQuery.unique(ret)}if(rparentsprev.test(name)){ret=ret.reverse()}}return this.pushStack(ret)}});var rnotwhite=/\S+/g;var optionsCache={};function createOptions(options){var object=optionsCache[options]={};jQuery.each(options.match(rnotwhite)||[],function(_,flag){object[flag]=true});return object}jQuery.Callbacks=function(options){options=typeof options==="string"?optionsCache[options]||createOptions(options):jQuery.extend({},options);var firing,memory,fired,firingLength,firingIndex,firingStart,list=[],stack=!options.once&&[],fire=function(data){memory=options.memory&&data;fired=true;firingIndex=firingStart||0;firingStart=0;firingLength=list.length;firing=true;for(;list&&firingIndex-1){list.splice(index,1);if(firing){if(index<=firingLength){firingLength--}if(index<=firingIndex){firingIndex--}}}})}return this},has:function(fn){return fn?jQuery.inArray(fn,list)>-1:!!(list&&list.length)},empty:function(){list=[];firingLength=0;return this},disable:function(){list=stack=memory=undefined;return this},disabled:function(){return!list},lock:function(){stack=undefined;if(!memory){self.disable()}return this},locked:function(){return!stack},fireWith:function(context,args){if(list&&(!fired||stack)){args=args||[];args=[context,args.slice?args.slice():args];if(firing){stack.push(args)}else{fire(args)}}return this},fire:function(){self.fireWith(this,arguments);return this},fired:function(){return!!fired}};return self};jQuery.extend({Deferred:function(func){var tuples=[["resolve","done",jQuery.Callbacks("once memory"),"resolved"],["reject","fail",jQuery.Callbacks("once memory"),"rejected"],["notify","progress",jQuery.Callbacks("memory")]],state="pending",promise={state:function(){return state},always:function(){deferred.done(arguments).fail(arguments);return this},then:function(){var fns=arguments;return jQuery.Deferred(function(newDefer){jQuery.each(tuples,function(i,tuple){var fn=jQuery.isFunction(fns[i])&&fns[i];deferred[tuple[1]](function(){var returned=fn&&fn.apply(this,arguments);if(returned&&jQuery.isFunction(returned.promise)){returned.promise().done(newDefer.resolve).fail(newDefer.reject).progress(newDefer.notify)}else{newDefer[tuple[0]+"With"](this===promise?newDefer.promise():this,fn?[returned]:arguments)}})});fns=null}).promise()},promise:function(obj){return obj!=null?jQuery.extend(obj,promise):promise}},deferred={};promise.pipe=promise.then;jQuery.each(tuples,function(i,tuple){var list=tuple[2],stateString=tuple[3];promise[tuple[1]]=list.add;if(stateString){list.add(function(){state=stateString},tuples[i^1][2].disable,tuples[2][2].lock)}deferred[tuple[0]]=function(){deferred[tuple[0]+"With"](this===deferred?promise:this,arguments);return this};deferred[tuple[0]+"With"]=list.fireWith});promise.promise(deferred);if(func){func.call(deferred,deferred)}return deferred},when:function(subordinate){var i=0,resolveValues=slice.call(arguments),length=resolveValues.length,remaining=length!==1||subordinate&&jQuery.isFunction(subordinate.promise)?length:0,deferred=remaining===1?subordinate:jQuery.Deferred(),updateFunc=function(i,contexts,values){return function(value){contexts[i]=this;values[i]=arguments.length>1?slice.call(arguments):value;if(values===progressValues){deferred.notifyWith(contexts,values)}else if(!--remaining){deferred.resolveWith(contexts,values)}}},progressValues,progressContexts,resolveContexts;if(length>1){progressValues=new Array(length);progressContexts=new Array(length);resolveContexts=new Array(length);for(;i0){return}readyList.resolveWith(document,[jQuery]);if(jQuery.fn.triggerHandler){jQuery(document).triggerHandler("ready");jQuery(document).off("ready")}}});function detach(){if(document.addEventListener){document.removeEventListener("DOMContentLoaded",completed,false);window.removeEventListener("load",completed,false)}else{document.detachEvent("onreadystatechange",completed);window.detachEvent("onload",completed)}}function completed(){if(document.addEventListener||event.type==="load"||document.readyState==="complete"){detach();jQuery.ready()}}jQuery.ready.promise=function(obj){if(!readyList){readyList=jQuery.Deferred();if(document.readyState==="complete"){setTimeout(jQuery.ready)}else if(document.addEventListener){document.addEventListener("DOMContentLoaded",completed,false);window.addEventListener("load",completed,false)}else{document.attachEvent("onreadystatechange",completed);window.attachEvent("onload",completed);var top=false;try{top=window.frameElement==null&&document.documentElement}catch(e){}if(top&&top.doScroll){(function doScrollCheck(){if(!jQuery.isReady){try{top.doScroll("left")}catch(e){return setTimeout(doScrollCheck,50)}detach();jQuery.ready()}})()}}}return readyList.promise(obj)};var strundefined=typeof undefined;var i;for(i in jQuery(support)){break}support.ownLast=i!=="0";support.inlineBlockNeedsLayout=false;jQuery(function(){var val,div,body,container;body=document.getElementsByTagName("body")[0];if(!body||!body.style){return}div=document.createElement("div");container=document.createElement("div");container.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px";body.appendChild(container).appendChild(div);if(typeof div.style.zoom!==strundefined){div.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1";support.inlineBlockNeedsLayout=val=div.offsetWidth===3;if(val){body.style.zoom=1}}body.removeChild(container)});(function(){var div=document.createElement("div");if(support.deleteExpando==null){support.deleteExpando=true;try{delete div.test}catch(e){support.deleteExpando=false}}div=null})();jQuery.acceptData=function(elem){var noData=jQuery.noData[(elem.nodeName+" ").toLowerCase()],nodeType=+elem.nodeType||1;return nodeType!==1&&nodeType!==9?false:!noData||noData!==true&&elem.getAttribute("classid")===noData};var rbrace=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,rmultiDash=/([A-Z])/g;function dataAttr(elem,key,data){if(data===undefined&&elem.nodeType===1){var name="data-"+key.replace(rmultiDash,"-$1").toLowerCase();data=elem.getAttribute(name);if(typeof data==="string"){try{data=data==="true"?true:data==="false"?false:data==="null"?null:+data+""===data?+data:rbrace.test(data)?jQuery.parseJSON(data):data}catch(e){}jQuery.data(elem,key,data)}else{data=undefined}}return data}function isEmptyDataObject(obj){var name;for(name in obj){if(name==="data"&&jQuery.isEmptyObject(obj[name])){continue}if(name!=="toJSON"){return false}}return true}function internalData(elem,name,data,pvt){if(!jQuery.acceptData(elem)){return}var ret,thisCache,internalKey=jQuery.expando,isNode=elem.nodeType,cache=isNode?jQuery.cache:elem,id=isNode?elem[internalKey]:elem[internalKey]&&internalKey;if((!id||!cache[id]||!pvt&&!cache[id].data)&&data===undefined&&typeof name==="string"){return}if(!id){if(isNode){id=elem[internalKey]=deletedIds.pop()||jQuery.guid++}else{id=internalKey}}if(!cache[id]){cache[id]=isNode?{}:{toJSON:jQuery.noop}}if(typeof name==="object"||typeof name==="function"){if(pvt){cache[id]=jQuery.extend(cache[id],name)}else{cache[id].data=jQuery.extend(cache[id].data,name)}}thisCache=cache[id];if(!pvt){if(!thisCache.data){thisCache.data={}}thisCache=thisCache.data}if(data!==undefined){thisCache[jQuery.camelCase(name)]=data}if(typeof name==="string"){ret=thisCache[name];if(ret==null){ret=thisCache[jQuery.camelCase(name)]}}else{ret=thisCache}return ret}function internalRemoveData(elem,name,pvt){if(!jQuery.acceptData(elem)){return}var thisCache,i,isNode=elem.nodeType,cache=isNode?jQuery.cache:elem,id=isNode?elem[jQuery.expando]:jQuery.expando;if(!cache[id]){return}if(name){thisCache=pvt?cache[id]:cache[id].data;if(thisCache){if(!jQuery.isArray(name)){if(name in thisCache){name=[name]}else{name=jQuery.camelCase(name);if(name in thisCache){name=[name]}else{name=name.split(" ")}}}else{name=name.concat(jQuery.map(name,jQuery.camelCase))}i=name.length;while(i--){delete thisCache[name[i]]}if(pvt?!isEmptyDataObject(thisCache):!jQuery.isEmptyObject(thisCache)){return}}}if(!pvt){delete cache[id].data;if(!isEmptyDataObject(cache[id])){return}}if(isNode){jQuery.cleanData([elem],true)}else if(support.deleteExpando||cache!=cache.window){delete cache[id]}else{cache[id]=null}}jQuery.extend({cache:{},noData:{"applet ":true,"embed ":true,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(elem){elem=elem.nodeType?jQuery.cache[elem[jQuery.expando]]:elem[jQuery.expando];return!!elem&&!isEmptyDataObject(elem)},data:function(elem,name,data){return internalData(elem,name,data)},removeData:function(elem,name){return internalRemoveData(elem,name)},_data:function(elem,name,data){return internalData(elem,name,data,true)},_removeData:function(elem,name){return internalRemoveData(elem,name,true)}});jQuery.fn.extend({data:function(key,value){var i,name,data,elem=this[0],attrs=elem&&elem.attributes;if(key===undefined){if(this.length){data=jQuery.data(elem);if(elem.nodeType===1&&!jQuery._data(elem,"parsedAttrs")){i=attrs.length;while(i--){if(attrs[i]){name=attrs[i].name;if(name.indexOf("data-")===0){name=jQuery.camelCase(name.slice(5));dataAttr(elem,name,data[name])}}}jQuery._data(elem,"parsedAttrs",true)}}return data}if(typeof key==="object"){return this.each(function(){jQuery.data(this,key)})}return arguments.length>1?this.each(function(){jQuery.data(this,key,value)}):elem?dataAttr(elem,key,jQuery.data(elem,key)):undefined},removeData:function(key){return this.each(function(){jQuery.removeData(this,key)})}});jQuery.extend({queue:function(elem,type,data){var queue;if(elem){type=(type||"fx")+"queue";queue=jQuery._data(elem,type);if(data){if(!queue||jQuery.isArray(data)){queue=jQuery._data(elem,type,jQuery.makeArray(data))}else{queue.push(data)}}return queue||[]}},dequeue:function(elem,type){type=type||"fx";var queue=jQuery.queue(elem,type),startLength=queue.length,fn=queue.shift(),hooks=jQuery._queueHooks(elem,type),next=function(){jQuery.dequeue(elem,type)};if(fn==="inprogress"){fn=queue.shift();startLength--}if(fn){if(type==="fx"){queue.unshift("inprogress")}delete hooks.stop;fn.call(elem,next,hooks)}if(!startLength&&hooks){hooks.empty.fire()}},_queueHooks:function(elem,type){var key=type+"queueHooks";return jQuery._data(elem,key)||jQuery._data(elem,key,{empty:jQuery.Callbacks("once memory").add(function(){jQuery._removeData(elem,type+"queue");jQuery._removeData(elem,key)})})}});jQuery.fn.extend({queue:function(type,data){var setter=2;if(typeof type!=="string"){data=type;type="fx";setter--}if(arguments.length
a";support.leadingWhitespace=div.firstChild.nodeType===3;support.tbody=!div.getElementsByTagName("tbody").length;support.htmlSerialize=!!div.getElementsByTagName("link").length;support.html5Clone=document.createElement("nav").cloneNode(true).outerHTML!=="<:nav>";input.type="checkbox";input.checked=true;fragment.appendChild(input);support.appendChecked=input.checked;div.innerHTML="";support.noCloneChecked=!!div.cloneNode(true).lastChild.defaultValue;fragment.appendChild(div);div.innerHTML="";support.checkClone=div.cloneNode(true).cloneNode(true).lastChild.checked;support.noCloneEvent=true;if(div.attachEvent){div.attachEvent("onclick",function(){support.noCloneEvent=false});div.cloneNode(true).click()}if(support.deleteExpando==null){support.deleteExpando=true;try{delete div.test}catch(e){support.deleteExpando=false}}})();(function(){var i,eventName,div=document.createElement("div");for(i in{submit:true,change:true,focusin:true}){eventName="on"+i;if(!(support[i+"Bubbles"]=eventName in window)){div.setAttribute(eventName,"t");support[i+"Bubbles"]=div.attributes[eventName].expando===false}}div=null})();var rformElems=/^(?:input|select|textarea)$/i,rkeyEvent=/^key/,rmouseEvent=/^(?:mouse|pointer|contextmenu)|click/,rfocusMorph=/^(?:focusinfocus|focusoutblur)$/,rtypenamespace=/^([^.]*)(?:\.(.+)|)$/;function returnTrue(){return true}function returnFalse(){return false}function safeActiveElement(){try{return document.activeElement}catch(err){}}jQuery.event={global:{},add:function(elem,types,handler,data,selector){var tmp,events,t,handleObjIn,special,eventHandle,handleObj,handlers,type,namespaces,origType,elemData=jQuery._data(elem);if(!elemData){return}if(handler.handler){handleObjIn=handler;handler=handleObjIn.handler;selector=handleObjIn.selector}if(!handler.guid){handler.guid=jQuery.guid++}if(!(events=elemData.events)){events=elemData.events={}}if(!(eventHandle=elemData.handle)){eventHandle=elemData.handle=function(e){return typeof jQuery!==strundefined&&(!e||jQuery.event.triggered!==e.type)?jQuery.event.dispatch.apply(eventHandle.elem,arguments):undefined};eventHandle.elem=elem}types=(types||"").match(rnotwhite)||[""];t=types.length;while(t--){tmp=rtypenamespace.exec(types[t])||[];type=origType=tmp[1];namespaces=(tmp[2]||"").split(".").sort();if(!type){continue}special=jQuery.event.special[type]||{};type=(selector?special.delegateType:special.bindType)||type;special=jQuery.event.special[type]||{};handleObj=jQuery.extend({type:type,origType:origType,data:data,handler:handler,guid:handler.guid,selector:selector,needsContext:selector&&jQuery.expr.match.needsContext.test(selector),namespace:namespaces.join(".")},handleObjIn);if(!(handlers=events[type])){handlers=events[type]=[];handlers.delegateCount=0;if(!special.setup||special.setup.call(elem,data,namespaces,eventHandle)===false){if(elem.addEventListener){elem.addEventListener(type,eventHandle,false)}else if(elem.attachEvent){elem.attachEvent("on"+type,eventHandle)}}}if(special.add){special.add.call(elem,handleObj);if(!handleObj.handler.guid){handleObj.handler.guid=handler.guid}}if(selector){handlers.splice(handlers.delegateCount++,0,handleObj)}else{handlers.push(handleObj)}jQuery.event.global[type]=true}elem=null},remove:function(elem,types,handler,selector,mappedTypes){var j,handleObj,tmp,origCount,t,events,special,handlers,type,namespaces,origType,elemData=jQuery.hasData(elem)&&jQuery._data(elem);if(!elemData||!(events=elemData.events)){return}types=(types||"").match(rnotwhite)||[""];t=types.length;while(t--){tmp=rtypenamespace.exec(types[t])||[];type=origType=tmp[1];namespaces=(tmp[2]||"").split(".").sort();if(!type){for(type in events){jQuery.event.remove(elem,type+types[t],handler,selector,true)}continue}special=jQuery.event.special[type]||{};type=(selector?special.delegateType:special.bindType)||type;handlers=events[type]||[];tmp=tmp[2]&&new RegExp("(^|\\.)"+namespaces.join("\\.(?:.*\\.|)")+"(\\.|$)");origCount=j=handlers.length;while(j--){handleObj=handlers[j];if((mappedTypes||origType===handleObj.origType)&&(!handler||handler.guid===handleObj.guid)&&(!tmp||tmp.test(handleObj.namespace))&&(!selector||selector===handleObj.selector||selector==="**"&&handleObj.selector)){handlers.splice(j,1);if(handleObj.selector){handlers.delegateCount--}if(special.remove){special.remove.call(elem,handleObj)}}}if(origCount&&!handlers.length){if(!special.teardown||special.teardown.call(elem,namespaces,elemData.handle)===false){jQuery.removeEvent(elem,type,elemData.handle)}delete events[type]}}if(jQuery.isEmptyObject(events)){delete elemData.handle;jQuery._removeData(elem,"events")}},trigger:function(event,data,elem,onlyHandlers){var handle,ontype,cur,bubbleType,special,tmp,i,eventPath=[elem||document],type=hasOwn.call(event,"type")?event.type:event,namespaces=hasOwn.call(event,"namespace")?event.namespace.split("."):[];cur=tmp=elem=elem||document;if(elem.nodeType===3||elem.nodeType===8){return}if(rfocusMorph.test(type+jQuery.event.triggered)){return}if(type.indexOf(".")>=0){namespaces=type.split(".");type=namespaces.shift();namespaces.sort()}ontype=type.indexOf(":")<0&&"on"+type;event=event[jQuery.expando]?event:new jQuery.Event(type,typeof event==="object"&&event);event.isTrigger=onlyHandlers?2:3;event.namespace=namespaces.join(".");event.namespace_re=event.namespace?new RegExp("(^|\\.)"+namespaces.join("\\.(?:.*\\.|)")+"(\\.|$)"):null;event.result=undefined;if(!event.target){event.target=elem}data=data==null?[event]:jQuery.makeArray(data,[event]);special=jQuery.event.special[type]||{};if(!onlyHandlers&&special.trigger&&special.trigger.apply(elem,data)===false){return}if(!onlyHandlers&&!special.noBubble&&!jQuery.isWindow(elem)){bubbleType=special.delegateType||type;if(!rfocusMorph.test(bubbleType+type)){cur=cur.parentNode}for(;cur;cur=cur.parentNode){eventPath.push(cur);tmp=cur}if(tmp===(elem.ownerDocument||document)){eventPath.push(tmp.defaultView||tmp.parentWindow||window)}}i=0;while((cur=eventPath[i++])&&!event.isPropagationStopped()){event.type=i>1?bubbleType:special.bindType||type;handle=(jQuery._data(cur,"events")||{})[event.type]&&jQuery._data(cur,"handle");if(handle){handle.apply(cur,data)}handle=ontype&&cur[ontype];if(handle&&handle.apply&&jQuery.acceptData(cur)){event.result=handle.apply(cur,data);if(event.result===false){event.preventDefault()}}}event.type=type;if(!onlyHandlers&&!event.isDefaultPrevented()){if((!special._default||special._default.apply(eventPath.pop(),data)===false)&&jQuery.acceptData(elem)){if(ontype&&elem[type]&&!jQuery.isWindow(elem)){tmp=elem[ontype];if(tmp){elem[ontype]=null}jQuery.event.triggered=type;try{elem[type]()}catch(e){}jQuery.event.triggered=undefined;if(tmp){elem[ontype]=tmp}}}}return event.result},dispatch:function(event){event=jQuery.event.fix(event);var i,ret,handleObj,matched,j,handlerQueue=[],args=slice.call(arguments),handlers=(jQuery._data(this,"events")||{})[event.type]||[],special=jQuery.event.special[event.type]||{};args[0]=event;event.delegateTarget=this;if(special.preDispatch&&special.preDispatch.call(this,event)===false){return}handlerQueue=jQuery.event.handlers.call(this,event,handlers);i=0;while((matched=handlerQueue[i++])&&!event.isPropagationStopped()){event.currentTarget=matched.elem;j=0;while((handleObj=matched.handlers[j++])&&!event.isImmediatePropagationStopped()){if(!event.namespace_re||event.namespace_re.test(handleObj.namespace)){event.handleObj=handleObj;event.data=handleObj.data;ret=((jQuery.event.special[handleObj.origType]||{}).handle||handleObj.handler).apply(matched.elem,args);if(ret!==undefined){if((event.result=ret)===false){event.preventDefault();event.stopPropagation()}}}}}if(special.postDispatch){special.postDispatch.call(this,event)}return event.result},handlers:function(event,handlers){var sel,handleObj,matches,i,handlerQueue=[],delegateCount=handlers.delegateCount,cur=event.target;if(delegateCount&&cur.nodeType&&(!event.button||event.type!=="click")){for(;cur!=this;cur=cur.parentNode||this){if(cur.nodeType===1&&(cur.disabled!==true||event.type!=="click")){matches=[];for(i=0;i=0:jQuery.find(sel,this,null,[cur]).length}if(matches[sel]){matches.push(handleObj)}}if(matches.length){handlerQueue.push({elem:cur,handlers:matches})}}}}if(delegateCount]","i"),rleadingWhitespace=/^\s+/,rxhtmlTag=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,rtagName=/<([\w:]+)/,rtbody=/\s*$/g,wrapMap={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:support.htmlSerialize?[0,"",""]:[1,"X
","
"]},safeFragment=createSafeFragment(document),fragmentDiv=safeFragment.appendChild(document.createElement("div"));wrapMap.optgroup=wrapMap.option;wrapMap.tbody=wrapMap.tfoot=wrapMap.colgroup=wrapMap.caption=wrapMap.thead;wrapMap.th=wrapMap.td;function getAll(context,tag){var elems,elem,i=0,found=typeof context.getElementsByTagName!==strundefined?context.getElementsByTagName(tag||"*"):typeof context.querySelectorAll!==strundefined?context.querySelectorAll(tag||"*"):undefined;if(!found){for(found=[],elems=context.childNodes||context;(elem=elems[i])!=null;i++){if(!tag||jQuery.nodeName(elem,tag)){found.push(elem)}else{jQuery.merge(found,getAll(elem,tag))}}}return tag===undefined||tag&&jQuery.nodeName(context,tag)?jQuery.merge([context],found):found}function fixDefaultChecked(elem){if(rcheckableType.test(elem.type)){elem.defaultChecked=elem.checked}}function manipulationTarget(elem,content){return jQuery.nodeName(elem,"table")&&jQuery.nodeName(content.nodeType!==11?content:content.firstChild,"tr")?elem.getElementsByTagName("tbody")[0]||elem.appendChild(elem.ownerDocument.createElement("tbody")):elem}function disableScript(elem){elem.type=(jQuery.find.attr(elem,"type")!==null)+"/"+elem.type;return elem}function restoreScript(elem){var match=rscriptTypeMasked.exec(elem.type);if(match){elem.type=match[1]}else{elem.removeAttribute("type")}return elem}function setGlobalEval(elems,refElements){var elem,i=0;for(;(elem=elems[i])!=null;i++){jQuery._data(elem,"globalEval",!refElements||jQuery._data(refElements[i],"globalEval"))}}function cloneCopyEvent(src,dest){if(dest.nodeType!==1||!jQuery.hasData(src)){return}var type,i,l,oldData=jQuery._data(src),curData=jQuery._data(dest,oldData),events=oldData.events;if(events){delete curData.handle;curData.events={};for(type in events){for(i=0,l=events[type].length;i")){clone=elem.cloneNode(true)}else{fragmentDiv.innerHTML=elem.outerHTML;fragmentDiv.removeChild(clone=fragmentDiv.firstChild)}if((!support.noCloneEvent||!support.noCloneChecked)&&(elem.nodeType===1||elem.nodeType===11)&&!jQuery.isXMLDoc(elem)){destElements=getAll(clone);srcElements=getAll(elem);for(i=0;(node=srcElements[i])!=null;++i){if(destElements[i]){fixCloneNodeIssues(node,destElements[i])}}}if(dataAndEvents){if(deepDataAndEvents){srcElements=srcElements||getAll(elem);destElements=destElements||getAll(clone);for(i=0;(node=srcElements[i])!=null;i++){cloneCopyEvent(node,destElements[i])}}else{cloneCopyEvent(elem,clone)}}destElements=getAll(clone,"script");if(destElements.length>0){setGlobalEval(destElements,!inPage&&getAll(elem,"script"))}destElements=srcElements=node=null;return clone},buildFragment:function(elems,context,scripts,selection){var j,elem,contains,tmp,tag,tbody,wrap,l=elems.length,safe=createSafeFragment(context),nodes=[],i=0;for(;i")+wrap[2];j=wrap[0];while(j--){tmp=tmp.lastChild}if(!support.leadingWhitespace&&rleadingWhitespace.test(elem)){nodes.push(context.createTextNode(rleadingWhitespace.exec(elem)[0]))}if(!support.tbody){elem=tag==="table"&&!rtbody.test(elem)?tmp.firstChild:wrap[1]===""&&!rtbody.test(elem)?tmp:0;j=elem&&elem.childNodes.length;while(j--){if(jQuery.nodeName(tbody=elem.childNodes[j],"tbody")&&!tbody.childNodes.length){elem.removeChild(tbody)}}}jQuery.merge(nodes,tmp.childNodes);tmp.textContent="";while(tmp.firstChild){tmp.removeChild(tmp.firstChild)}tmp=safe.lastChild}}}if(tmp){safe.removeChild(tmp)}if(!support.appendChecked){jQuery.grep(getAll(nodes,"input"),fixDefaultChecked)}i=0;while(elem=nodes[i++]){if(selection&&jQuery.inArray(elem,selection)!==-1){continue}contains=jQuery.contains(elem.ownerDocument,elem);tmp=getAll(safe.appendChild(elem),"script");if(contains){setGlobalEval(tmp)}if(scripts){j=0;while(elem=tmp[j++]){if(rscriptType.test(elem.type||"")){scripts.push(elem)}}}}tmp=null;return safe},cleanData:function(elems,acceptData){var elem,type,id,data,i=0,internalKey=jQuery.expando,cache=jQuery.cache,deleteExpando=support.deleteExpando,special=jQuery.event.special;for(;(elem=elems[i])!=null;i++){if(acceptData||jQuery.acceptData(elem)){id=elem[internalKey];data=id&&cache[id];if(data){if(data.events){for(type in data.events){if(special[type]){jQuery.event.remove(elem,type)}else{jQuery.removeEvent(elem,type,data.handle)}}}if(cache[id]){delete cache[id];if(deleteExpando){delete elem[internalKey]}else if(typeof elem.removeAttribute!==strundefined){elem.removeAttribute(internalKey)}else{elem[internalKey]=null}deletedIds.push(id)}}}}}});jQuery.fn.extend({text:function(value){return access(this,function(value){return value===undefined?jQuery.text(this):this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(value))},null,value,arguments.length)},append:function(){return this.domManip(arguments,function(elem){if(this.nodeType===1||this.nodeType===11||this.nodeType===9){var target=manipulationTarget(this,elem);target.appendChild(elem)}})},prepend:function(){return this.domManip(arguments,function(elem){if(this.nodeType===1||this.nodeType===11||this.nodeType===9){var target=manipulationTarget(this,elem);target.insertBefore(elem,target.firstChild)}})},before:function(){return this.domManip(arguments,function(elem){if(this.parentNode){this.parentNode.insertBefore(elem,this)}})},after:function(){return this.domManip(arguments,function(elem){if(this.parentNode){this.parentNode.insertBefore(elem,this.nextSibling)}})},remove:function(selector,keepData){var elem,elems=selector?jQuery.filter(selector,this):this,i=0;for(;(elem=elems[i])!=null;i++){if(!keepData&&elem.nodeType===1){jQuery.cleanData(getAll(elem))}if(elem.parentNode){if(keepData&&jQuery.contains(elem.ownerDocument,elem)){setGlobalEval(getAll(elem,"script"))}elem.parentNode.removeChild(elem)}}return this},empty:function(){var elem,i=0;for(;(elem=this[i])!=null;i++){if(elem.nodeType===1){jQuery.cleanData(getAll(elem,false))}while(elem.firstChild){elem.removeChild(elem.firstChild)}if(elem.options&&jQuery.nodeName(elem,"select")){elem.options.length=0}}return this},clone:function(dataAndEvents,deepDataAndEvents){dataAndEvents=dataAndEvents==null?false:dataAndEvents;deepDataAndEvents=deepDataAndEvents==null?dataAndEvents:deepDataAndEvents;return this.map(function(){return jQuery.clone(this,dataAndEvents,deepDataAndEvents)})},html:function(value){return access(this,function(value){var elem=this[0]||{},i=0,l=this.length;if(value===undefined){return elem.nodeType===1?elem.innerHTML.replace(rinlinejQuery,""):undefined}if(typeof value==="string"&&!rnoInnerhtml.test(value)&&(support.htmlSerialize||!rnoshimcache.test(value))&&(support.leadingWhitespace||!rleadingWhitespace.test(value))&&!wrapMap[(rtagName.exec(value)||["",""])[1].toLowerCase()]){value=value.replace(rxhtmlTag,"<$1>");try{for(;i1&&typeof value==="string"&&!support.checkClone&&rchecked.test(value)){return this.each(function(index){var self=set.eq(index);if(isFunction){args[0]=value.call(this,index,self.html())}self.domManip(args,callback)})}if(l){fragment=jQuery.buildFragment(args,this[0].ownerDocument,false,this);first=fragment.firstChild;if(fragment.childNodes.length===1){fragment=first}if(first){scripts=jQuery.map(getAll(fragment,"script"),disableScript);hasScripts=scripts.length;for(;i")).appendTo(doc.documentElement);doc=(iframe[0].contentWindow||iframe[0].contentDocument).document;doc.write();doc.close();display=actualDisplay(nodeName,doc);iframe.detach()}elemdisplay[nodeName]=display}return display}(function(){var shrinkWrapBlocksVal;support.shrinkWrapBlocks=function(){if(shrinkWrapBlocksVal!=null){return shrinkWrapBlocksVal}shrinkWrapBlocksVal=false;var div,body,container;body=document.getElementsByTagName("body")[0];if(!body||!body.style){return}div=document.createElement("div");container=document.createElement("div");container.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px";body.appendChild(container).appendChild(div);if(typeof div.style.zoom!==strundefined){div.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;"+"box-sizing:content-box;display:block;margin:0;border:0;"+"padding:1px;width:1px;zoom:1";div.appendChild(document.createElement("div")).style.width="5px";shrinkWrapBlocksVal=div.offsetWidth!==3}body.removeChild(container);return shrinkWrapBlocksVal}})();var rmargin=/^margin/;var rnumnonpx=new RegExp("^("+pnum+")(?!px)[a-z%]+$","i");var getStyles,curCSS,rposition=/^(top|right|bottom|left)$/;if(window.getComputedStyle){getStyles=function(elem){if(elem.ownerDocument.defaultView.opener){return elem.ownerDocument.defaultView.getComputedStyle(elem,null)}return window.getComputedStyle(elem,null)};curCSS=function(elem,name,computed){var width,minWidth,maxWidth,ret,style=elem.style;computed=computed||getStyles(elem);ret=computed?computed.getPropertyValue(name)||computed[name]:undefined;if(computed){if(ret===""&&!jQuery.contains(elem.ownerDocument,elem)){ret=jQuery.style(elem,name)}if(rnumnonpx.test(ret)&&rmargin.test(name)){width=style.width;minWidth=style.minWidth;maxWidth=style.maxWidth;style.minWidth=style.maxWidth=style.width=ret;ret=computed.width;style.width=width;style.minWidth=minWidth;style.maxWidth=maxWidth}}return ret===undefined?ret:ret+""}}else if(document.documentElement.currentStyle){getStyles=function(elem){return elem.currentStyle};curCSS=function(elem,name,computed){var left,rs,rsLeft,ret,style=elem.style;computed=computed||getStyles(elem);ret=computed?computed[name]:undefined;if(ret==null&&style&&style[name]){ret=style[name]}if(rnumnonpx.test(ret)&&!rposition.test(name)){left=style.left;rs=elem.runtimeStyle;rsLeft=rs&&rs.left;if(rsLeft){rs.left=elem.currentStyle.left}style.left=name==="fontSize"?"1em":ret;ret=style.pixelLeft+"px";style.left=left;if(rsLeft){rs.left=rsLeft}}return ret===undefined?ret:ret+""||"auto"}}function addGetHookIf(conditionFn,hookFn){return{get:function(){var condition=conditionFn();if(condition==null){return}if(condition){delete this.get;return}return(this.get=hookFn).apply(this,arguments)}}}(function(){var div,style,a,pixelPositionVal,boxSizingReliableVal,reliableHiddenOffsetsVal,reliableMarginRightVal;div=document.createElement("div");div.innerHTML="
a";a=div.getElementsByTagName("a")[0];style=a&&a.style;if(!style){return}style.cssText="float:left;opacity:.5";support.opacity=style.opacity==="0.5";support.cssFloat=!!style.cssFloat;div.style.backgroundClip="content-box";div.cloneNode(true).style.backgroundClip="";support.clearCloneStyle=div.style.backgroundClip==="content-box";support.boxSizing=style.boxSizing===""||style.MozBoxSizing===""||style.WebkitBoxSizing==="";jQuery.extend(support,{reliableHiddenOffsets:function(){if(reliableHiddenOffsetsVal==null){computeStyleTests()}return reliableHiddenOffsetsVal},boxSizingReliable:function(){if(boxSizingReliableVal==null){computeStyleTests()}return boxSizingReliableVal},pixelPosition:function(){if(pixelPositionVal==null){computeStyleTests()}return pixelPositionVal},reliableMarginRight:function(){if(reliableMarginRightVal==null){computeStyleTests()}return reliableMarginRightVal}});function computeStyleTests(){var div,body,container,contents;body=document.getElementsByTagName("body")[0];if(!body||!body.style){return}div=document.createElement("div");container=document.createElement("div");container.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px";body.appendChild(container).appendChild(div);div.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;"+"box-sizing:border-box;display:block;margin-top:1%;top:1%;"+"border:1px;padding:1px;width:4px;position:absolute";pixelPositionVal=boxSizingReliableVal=false;reliableMarginRightVal=true;if(window.getComputedStyle){pixelPositionVal=(window.getComputedStyle(div,null)||{}).top!=="1%";boxSizingReliableVal=(window.getComputedStyle(div,null)||{width:"4px"}).width==="4px";contents=div.appendChild(document.createElement("div"));contents.style.cssText=div.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;"+"box-sizing:content-box;display:block;margin:0;border:0;padding:0";contents.style.marginRight=contents.style.width="0";div.style.width="1px";reliableMarginRightVal=!parseFloat((window.getComputedStyle(contents,null)||{}).marginRight);div.removeChild(contents)}div.innerHTML="
t
";contents=div.getElementsByTagName("td");contents[0].style.cssText="margin:0;border:0;padding:0;display:none";reliableHiddenOffsetsVal=contents[0].offsetHeight===0;if(reliableHiddenOffsetsVal){contents[0].style.display="";contents[1].style.display="none";reliableHiddenOffsetsVal=contents[0].offsetHeight===0}body.removeChild(container)}})();jQuery.swap=function(elem,options,callback,args){var ret,name,old={};for(name in options){old[name]=elem.style[name];elem.style[name]=options[name]}ret=callback.apply(elem,args||[]);for(name in options){elem.style[name]=old[name]}return ret};var ralpha=/alpha\([^)]*\)/i,ropacity=/opacity\s*=\s*([^)]*)/,rdisplayswap=/^(none|table(?!-c[ea]).+)/,rnumsplit=new RegExp("^("+pnum+")(.*)$","i"),rrelNum=new RegExp("^([+-])=("+pnum+")","i"),cssShow={position:"absolute",visibility:"hidden",display:"block"},cssNormalTransform={letterSpacing:"0",fontWeight:"400"},cssPrefixes=["Webkit","O","Moz","ms"];function vendorPropName(style,name){if(name in style){return name}var capName=name.charAt(0).toUpperCase()+name.slice(1),origName=name,i=cssPrefixes.length;while(i--){name=cssPrefixes[i]+capName;if(name in style){return name}}return origName}function showHide(elements,show){var display,elem,hidden,values=[],index=0,length=elements.length;for(;index=1||value==="")&&jQuery.trim(filter.replace(ralpha,""))===""&&style.removeAttribute){style.removeAttribute("filter");if(value===""||currentStyle&&!currentStyle.filter){return}}style.filter=ralpha.test(filter)?filter.replace(ralpha,opacity):filter+" "+opacity}}}jQuery.cssHooks.marginRight=addGetHookIf(support.reliableMarginRight,function(elem,computed){if(computed){return jQuery.swap(elem,{display:"inline-block"},curCSS,[elem,"marginRight"])}});jQuery.each({margin:"",padding:"",border:"Width"},function(prefix,suffix){jQuery.cssHooks[prefix+suffix]={expand:function(value){var i=0,expanded={},parts=typeof value==="string"?value.split(" "):[value];for(;i<4;i++){expanded[prefix+cssExpand[i]+suffix]=parts[i]||parts[i-2]||parts[0]}return expanded}};if(!rmargin.test(prefix)){jQuery.cssHooks[prefix+suffix].set=setPositiveNumber}});jQuery.fn.extend({css:function(name,value){return access(this,function(elem,name,value){var styles,len,map={},i=0;if(jQuery.isArray(name)){styles=getStyles(elem);len=name.length;for(;i1)},show:function(){return showHide(this,true)},hide:function(){return showHide(this)},toggle:function(state){if(typeof state==="boolean"){return state?this.show():this.hide()}return this.each(function(){if(isHidden(this)){jQuery(this).show()}else{jQuery(this).hide()}})}});function Tween(elem,options,prop,end,easing){return new Tween.prototype.init(elem,options,prop,end,easing)}jQuery.Tween=Tween;Tween.prototype={constructor:Tween,init:function(elem,options,prop,end,easing,unit){this.elem=elem;this.prop=prop;this.easing=easing||"swing";this.options=options;this.start=this.now=this.cur();this.end=end;this.unit=unit||(jQuery.cssNumber[prop]?"":"px")},cur:function(){var hooks=Tween.propHooks[this.prop];return hooks&&hooks.get?hooks.get(this):Tween.propHooks._default.get(this)},run:function(percent){var eased,hooks=Tween.propHooks[this.prop];if(this.options.duration){this.pos=eased=jQuery.easing[this.easing](percent,this.options.duration*percent,0,1,this.options.duration)}else{this.pos=eased=percent}this.now=(this.end-this.start)*eased+this.start;if(this.options.step){this.options.step.call(this.elem,this.now,this)}if(hooks&&hooks.set){hooks.set(this)}else{Tween.propHooks._default.set(this)}return this}};Tween.prototype.init.prototype=Tween.prototype;Tween.propHooks={_default:{get:function(tween){var result;if(tween.elem[tween.prop]!=null&&(!tween.elem.style||tween.elem.style[tween.prop]==null)){return tween.elem[tween.prop]}result=jQuery.css(tween.elem,tween.prop,"");return!result||result==="auto"?0:result},set:function(tween){if(jQuery.fx.step[tween.prop]){jQuery.fx.step[tween.prop](tween)}else if(tween.elem.style&&(tween.elem.style[jQuery.cssProps[tween.prop]]!=null||jQuery.cssHooks[tween.prop])){jQuery.style(tween.elem,tween.prop,tween.now+tween.unit)}else{tween.elem[tween.prop]=tween.now}}}};Tween.propHooks.scrollTop=Tween.propHooks.scrollLeft={set:function(tween){if(tween.elem.nodeType&&tween.elem.parentNode){tween.elem[tween.prop]=tween.now}}};jQuery.easing={linear:function(p){return p},swing:function(p){return.5-Math.cos(p*Math.PI)/2}};jQuery.fx=Tween.prototype.init;jQuery.fx.step={};var fxNow,timerId,rfxtypes=/^(?:toggle|show|hide)$/,rfxnum=new RegExp("^(?:([+-])=|)("+pnum+")([a-z%]*)$","i"),rrun=/queueHooks$/,animationPrefilters=[defaultPrefilter],tweeners={"*":[function(prop,value){var tween=this.createTween(prop,value),target=tween.cur(),parts=rfxnum.exec(value),unit=parts&&parts[3]||(jQuery.cssNumber[prop]?"":"px"),start=(jQuery.cssNumber[prop]||unit!=="px"&&+target)&&rfxnum.exec(jQuery.css(tween.elem,prop)),scale=1,maxIterations=20;if(start&&start[3]!==unit){unit=unit||start[3];parts=parts||[];start=+target||1;do{scale=scale||".5";start=start/scale;jQuery.style(tween.elem,prop,start+unit)}while(scale!==(scale=tween.cur()/target)&&scale!==1&&--maxIterations)}if(parts){start=tween.start=+start||+target||0;tween.unit=unit;tween.end=parts[1]?start+(parts[1]+1)*parts[2]:+parts[2]}return tween}]};function createFxNow(){setTimeout(function(){fxNow=undefined});return fxNow=jQuery.now()}function genFx(type,includeWidth){var which,attrs={height:type},i=0;includeWidth=includeWidth?1:0;for(;i<4;i+=2-includeWidth){which=cssExpand[i];attrs["margin"+which]=attrs["padding"+which]=type}if(includeWidth){attrs.opacity=attrs.width=type}return attrs}function createTween(value,prop,animation){var tween,collection=(tweeners[prop]||[]).concat(tweeners["*"]),index=0,length=collection.length;for(;index
a";a=div.getElementsByTagName("a")[0];select=document.createElement("select");opt=select.appendChild(document.createElement("option"));input=div.getElementsByTagName("input")[0];a.style.cssText="top:1px";support.getSetAttribute=div.className!=="t";support.style=/top/.test(a.getAttribute("style"));support.hrefNormalized=a.getAttribute("href")==="/a";support.checkOn=!!input.value;support.optSelected=opt.selected;support.enctype=!!document.createElement("form").enctype;select.disabled=true;support.optDisabled=!opt.disabled;input=document.createElement("input");input.setAttribute("value","");support.input=input.getAttribute("value")==="";input.value="t";input.setAttribute("type","radio");support.radioValue=input.value==="t"})();var rreturn=/\r/g;jQuery.fn.extend({val:function(value){var hooks,ret,isFunction,elem=this[0];if(!arguments.length){if(elem){hooks=jQuery.valHooks[elem.type]||jQuery.valHooks[elem.nodeName.toLowerCase()];if(hooks&&"get"in hooks&&(ret=hooks.get(elem,"value"))!==undefined){return ret}ret=elem.value;return typeof ret==="string"?ret.replace(rreturn,""):ret==null?"":ret}return}isFunction=jQuery.isFunction(value);return this.each(function(i){var val;if(this.nodeType!==1){return}if(isFunction){val=value.call(this,i,jQuery(this).val())}else{val=value}if(val==null){val=""}else if(typeof val==="number"){val+=""}else if(jQuery.isArray(val)){val=jQuery.map(val,function(value){return value==null?"":value+""})}hooks=jQuery.valHooks[this.type]||jQuery.valHooks[this.nodeName.toLowerCase()];if(!hooks||!("set"in hooks)||hooks.set(this,val,"value")===undefined){this.value=val}})}});jQuery.extend({valHooks:{option:{get:function(elem){var val=jQuery.find.attr(elem,"value");return val!=null?val:jQuery.trim(jQuery.text(elem))}},select:{get:function(elem){var value,option,options=elem.options,index=elem.selectedIndex,one=elem.type==="select-one"||index<0,values=one?null:[],max=one?index+1:options.length,i=index<0?max:one?index:0;for(;i=0){try{option.selected=optionSet=true}catch(_){option.scrollHeight}}else{option.selected=false}}if(!optionSet){elem.selectedIndex=-1}return options}}}});jQuery.each(["radio","checkbox"],function(){jQuery.valHooks[this]={set:function(elem,value){if(jQuery.isArray(value)){return elem.checked=jQuery.inArray(jQuery(elem).val(),value)>=0}}};if(!support.checkOn){jQuery.valHooks[this].get=function(elem){return elem.getAttribute("value")===null?"on":elem.value}}});var nodeHook,boolHook,attrHandle=jQuery.expr.attrHandle,ruseDefault=/^(?:checked|selected)$/i,getSetAttribute=support.getSetAttribute,getSetInput=support.input;jQuery.fn.extend({attr:function(name,value){return access(this,jQuery.attr,name,value,arguments.length>1)},removeAttr:function(name){return this.each(function(){jQuery.removeAttr(this,name)})}});jQuery.extend({attr:function(elem,name,value){var hooks,ret,nType=elem.nodeType;if(!elem||nType===3||nType===8||nType===2){return}if(typeof elem.getAttribute===strundefined){return jQuery.prop(elem,name,value)}if(nType!==1||!jQuery.isXMLDoc(elem)){name=name.toLowerCase();hooks=jQuery.attrHooks[name]||(jQuery.expr.match.bool.test(name)?boolHook:nodeHook)}if(value!==undefined){if(value===null){jQuery.removeAttr(elem,name)}else if(hooks&&"set"in hooks&&(ret=hooks.set(elem,value,name))!==undefined){return ret}else{elem.setAttribute(name,value+"");return value}}else if(hooks&&"get"in hooks&&(ret=hooks.get(elem,name))!==null){return ret}else{ret=jQuery.find.attr(elem,name);return ret==null?undefined:ret}},removeAttr:function(elem,value){var name,propName,i=0,attrNames=value&&value.match(rnotwhite);if(attrNames&&elem.nodeType===1){while(name=attrNames[i++]){propName=jQuery.propFix[name]||name;if(jQuery.expr.match.bool.test(name)){if(getSetInput&&getSetAttribute||!ruseDefault.test(name)){elem[propName]=false}else{elem[jQuery.camelCase("default-"+name)]=elem[propName]=false}}else{jQuery.attr(elem,name,"")}elem.removeAttribute(getSetAttribute?name:propName)}}},attrHooks:{type:{set:function(elem,value){if(!support.radioValue&&value==="radio"&&jQuery.nodeName(elem,"input")){var val=elem.value;elem.setAttribute("type",value);if(val){elem.value=val}return value}}}}});boolHook={set:function(elem,value,name){if(value===false){jQuery.removeAttr(elem,name)}else if(getSetInput&&getSetAttribute||!ruseDefault.test(name)){elem.setAttribute(!getSetAttribute&&jQuery.propFix[name]||name,name)}else{elem[jQuery.camelCase("default-"+name)]=elem[name]=true}return name}};jQuery.each(jQuery.expr.match.bool.source.match(/\w+/g),function(i,name){var getter=attrHandle[name]||jQuery.find.attr;attrHandle[name]=getSetInput&&getSetAttribute||!ruseDefault.test(name)?function(elem,name,isXML){var ret,handle;if(!isXML){handle=attrHandle[name];attrHandle[name]=ret;ret=getter(elem,name,isXML)!=null?name.toLowerCase():null;attrHandle[name]=handle}return ret}:function(elem,name,isXML){if(!isXML){return elem[jQuery.camelCase("default-"+name)]?name.toLowerCase():null}}});if(!getSetInput||!getSetAttribute){jQuery.attrHooks.value={set:function(elem,value,name){if(jQuery.nodeName(elem,"input")){elem.defaultValue=value}else{return nodeHook&&nodeHook.set(elem,value,name)}}}}if(!getSetAttribute){nodeHook={set:function(elem,value,name){var ret=elem.getAttributeNode(name);if(!ret){elem.setAttributeNode(ret=elem.ownerDocument.createAttribute(name))}ret.value=value+="";if(name==="value"||value===elem.getAttribute(name)){return value}}};attrHandle.id=attrHandle.name=attrHandle.coords=function(elem,name,isXML){var ret;if(!isXML){return(ret=elem.getAttributeNode(name))&&ret.value!==""?ret.value:null}};jQuery.valHooks.button={get:function(elem,name){var ret=elem.getAttributeNode(name);if(ret&&ret.specified){return ret.value}},set:nodeHook.set};jQuery.attrHooks.contenteditable={set:function(elem,value,name){nodeHook.set(elem,value===""?false:value,name)}};jQuery.each(["width","height"],function(i,name){jQuery.attrHooks[name]={set:function(elem,value){if(value===""){elem.setAttribute(name,"auto");return value}}}})}if(!support.style){jQuery.attrHooks.style={get:function(elem){return elem.style.cssText||undefined},set:function(elem,value){return elem.style.cssText=value+""}}}var rfocusable=/^(?:input|select|textarea|button|object)$/i,rclickable=/^(?:a|area)$/i;jQuery.fn.extend({prop:function(name,value){return access(this,jQuery.prop,name,value,arguments.length>1)},removeProp:function(name){name=jQuery.propFix[name]||name;return this.each(function(){try{this[name]=undefined;delete this[name]}catch(e){}})}});jQuery.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(elem,name,value){var ret,hooks,notxml,nType=elem.nodeType;if(!elem||nType===3||nType===8||nType===2){return}notxml=nType!==1||!jQuery.isXMLDoc(elem);if(notxml){name=jQuery.propFix[name]||name;hooks=jQuery.propHooks[name]}if(value!==undefined){return hooks&&"set"in hooks&&(ret=hooks.set(elem,value,name))!==undefined?ret:elem[name]=value}else{return hooks&&"get"in hooks&&(ret=hooks.get(elem,name))!==null?ret:elem[name]}},propHooks:{tabIndex:{get:function(elem){var tabindex=jQuery.find.attr(elem,"tabindex");return tabindex?parseInt(tabindex,10):rfocusable.test(elem.nodeName)||rclickable.test(elem.nodeName)&&elem.href?0:-1}}}});if(!support.hrefNormalized){jQuery.each(["href","src"],function(i,name){jQuery.propHooks[name]={get:function(elem){return elem.getAttribute(name,4)}}})}if(!support.optSelected){jQuery.propHooks.selected={get:function(elem){var parent=elem.parentNode;if(parent){parent.selectedIndex;if(parent.parentNode){parent.parentNode.selectedIndex}}return null}}}jQuery.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){jQuery.propFix[this.toLowerCase()]=this});if(!support.enctype){jQuery.propFix.enctype="encoding"}var rclass=/[\t\r\n\f]/g;jQuery.fn.extend({addClass:function(value){var classes,elem,cur,clazz,j,finalValue,i=0,len=this.length,proceed=typeof value==="string"&&value;if(jQuery.isFunction(value)){return this.each(function(j){jQuery(this).addClass(value.call(this,j,this.className))})}if(proceed){classes=(value||"").match(rnotwhite)||[];for(;i=0){cur=cur.replace(" "+clazz+" "," ")}}finalValue=value?jQuery.trim(cur):"";if(elem.className!==finalValue){elem.className=finalValue}}}}return this},toggleClass:function(value,stateVal){var type=typeof value;if(typeof stateVal==="boolean"&&type==="string"){return stateVal?this.addClass(value):this.removeClass(value)}if(jQuery.isFunction(value)){return this.each(function(i){jQuery(this).toggleClass(value.call(this,i,this.className,stateVal),stateVal)})}return this.each(function(){if(type==="string"){var className,i=0,self=jQuery(this),classNames=value.match(rnotwhite)||[];while(className=classNames[i++]){if(self.hasClass(className)){self.removeClass(className)}else{self.addClass(className)}}}else if(type===strundefined||type==="boolean"){if(this.className){jQuery._data(this,"__className__",this.className)}this.className=this.className||value===false?"":jQuery._data(this,"__className__")||""}})},hasClass:function(selector){var className=" "+selector+" ",i=0,l=this.length;for(;i=0){return true}}return false}});jQuery.each(("blur focus focusin focusout load resize scroll unload click dblclick "+"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave "+"change select submit keydown keypress keyup error contextmenu").split(" "),function(i,name){jQuery.fn[name]=function(data,fn){return arguments.length>0?this.on(name,null,data,fn):this.trigger(name)}});jQuery.fn.extend({hover:function(fnOver,fnOut){return this.mouseenter(fnOver).mouseleave(fnOut||fnOver)},bind:function(types,data,fn){return this.on(types,null,data,fn)},unbind:function(types,fn){return this.off(types,null,fn)},delegate:function(selector,types,data,fn){return this.on(types,selector,data,fn)},undelegate:function(selector,types,fn){return arguments.length===1?this.off(selector,"**"):this.off(types,selector||"**",fn)}});var nonce=jQuery.now();var rquery=/\?/;var rvalidtokens=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;jQuery.parseJSON=function(data){if(window.JSON&&window.JSON.parse){return window.JSON.parse(data+"")}var requireNonComma,depth=null,str=jQuery.trim(data+"");return str&&!jQuery.trim(str.replace(rvalidtokens,function(token,comma,open,close){if(requireNonComma&&comma){depth=0}if(depth===0){return token}requireNonComma=open||comma;depth+=!close-!open;return""}))?Function("return "+str)():jQuery.error("Invalid JSON: "+data)};jQuery.parseXML=function(data){var xml,tmp;if(!data||typeof data!=="string"){return null}try{if(window.DOMParser){tmp=new DOMParser;xml=tmp.parseFromString(data,"text/xml")}else{xml=new ActiveXObject("Microsoft.XMLDOM");xml.async="false";xml.loadXML(data)}}catch(e){xml=undefined}if(!xml||!xml.documentElement||xml.getElementsByTagName("parsererror").length){jQuery.error("Invalid XML: "+data)}return xml};var ajaxLocParts,ajaxLocation,rhash=/#.*$/,rts=/([?&])_=[^&]*/,rheaders=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,rlocalProtocol=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,rnoContent=/^(?:GET|HEAD)$/,rprotocol=/^\/\//,rurl=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,prefilters={},transports={},allTypes="*/".concat("*");try{ajaxLocation=location.href}catch(e){ajaxLocation=document.createElement("a");ajaxLocation.href="";ajaxLocation=ajaxLocation.href}ajaxLocParts=rurl.exec(ajaxLocation.toLowerCase())||[];function addToPrefiltersOrTransports(structure){return function(dataTypeExpression,func){if(typeof dataTypeExpression!=="string"){func=dataTypeExpression;dataTypeExpression="*"}var dataType,i=0,dataTypes=dataTypeExpression.toLowerCase().match(rnotwhite)||[];if(jQuery.isFunction(func)){while(dataType=dataTypes[i++]){if(dataType.charAt(0)==="+"){dataType=dataType.slice(1)||"*";(structure[dataType]=structure[dataType]||[]).unshift(func)}else{(structure[dataType]=structure[dataType]||[]).push(func)}}}}}function inspectPrefiltersOrTransports(structure,options,originalOptions,jqXHR){var inspected={},seekingTransport=structure===transports;function inspect(dataType){var selected;inspected[dataType]=true;jQuery.each(structure[dataType]||[],function(_,prefilterOrFactory){var dataTypeOrTransport=prefilterOrFactory(options,originalOptions,jqXHR);if(typeof dataTypeOrTransport==="string"&&!seekingTransport&&!inspected[dataTypeOrTransport]){options.dataTypes.unshift(dataTypeOrTransport);inspect(dataTypeOrTransport);return false}else if(seekingTransport){return!(selected=dataTypeOrTransport)}});return selected}return inspect(options.dataTypes[0])||!inspected["*"]&&inspect("*")}function ajaxExtend(target,src){var deep,key,flatOptions=jQuery.ajaxSettings.flatOptions||{};for(key in src){if(src[key]!==undefined){(flatOptions[key]?target:deep||(deep={}))[key]=src[key]}}if(deep){jQuery.extend(true,target,deep)}return target}function ajaxHandleResponses(s,jqXHR,responses){var firstDataType,ct,finalDataType,type,contents=s.contents,dataTypes=s.dataTypes;while(dataTypes[0]==="*"){dataTypes.shift();if(ct===undefined){ct=s.mimeType||jqXHR.getResponseHeader("Content-Type")}}if(ct){for(type in contents){if(contents[type]&&contents[type].test(ct)){dataTypes.unshift(type);break}}}if(dataTypes[0]in responses){finalDataType=dataTypes[0]}else{for(type in responses){if(!dataTypes[0]||s.converters[type+" "+dataTypes[0]]){finalDataType=type;break}if(!firstDataType){firstDataType=type}}finalDataType=finalDataType||firstDataType}if(finalDataType){if(finalDataType!==dataTypes[0]){dataTypes.unshift(finalDataType)}return responses[finalDataType]}}function ajaxConvert(s,response,jqXHR,isSuccess){var conv2,current,conv,tmp,prev,converters={},dataTypes=s.dataTypes.slice();if(dataTypes[1]){for(conv in s.converters){converters[conv.toLowerCase()]=s.converters[conv]}}current=dataTypes.shift();while(current){if(s.responseFields[current]){jqXHR[s.responseFields[current]]=response}if(!prev&&isSuccess&&s.dataFilter){response=s.dataFilter(response,s.dataType)}prev=current;current=dataTypes.shift();if(current){if(current==="*"){current=prev}else if(prev!=="*"&&prev!==current){conv=converters[prev+" "+current]||converters["* "+current];if(!conv){for(conv2 in converters){tmp=conv2.split(" ");if(tmp[1]===current){conv=converters[prev+" "+tmp[0]]||converters["* "+tmp[0]];if(conv){if(conv===true){conv=converters[conv2]}else if(converters[conv2]!==true){current=tmp[0];dataTypes.unshift(tmp[1])}break}}}}if(conv!==true){if(conv&&s["throws"]){response=conv(response)}else{try{response=conv(response)}catch(e){return{state:"parsererror",error:conv?e:"No conversion from "+prev+" to "+current}}}}}}}return{state:"success",data:response}}jQuery.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:ajaxLocation,type:"GET",isLocal:rlocalProtocol.test(ajaxLocParts[1]),global:true,processData:true,async:true,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":allTypes,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":true,"text json":jQuery.parseJSON,"text xml":jQuery.parseXML},flatOptions:{url:true,context:true}},ajaxSetup:function(target,settings){return settings?ajaxExtend(ajaxExtend(target,jQuery.ajaxSettings),settings):ajaxExtend(jQuery.ajaxSettings,target)},ajaxPrefilter:addToPrefiltersOrTransports(prefilters),ajaxTransport:addToPrefiltersOrTransports(transports),ajax:function(url,options){if(typeof url==="object"){options=url;url=undefined}options=options||{};var parts,i,cacheURL,responseHeadersString,timeoutTimer,fireGlobals,transport,responseHeaders,s=jQuery.ajaxSetup({},options),callbackContext=s.context||s,globalEventContext=s.context&&(callbackContext.nodeType||callbackContext.jquery)?jQuery(callbackContext):jQuery.event,deferred=jQuery.Deferred(),completeDeferred=jQuery.Callbacks("once memory"),statusCode=s.statusCode||{},requestHeaders={},requestHeadersNames={},state=0,strAbort="canceled",jqXHR={readyState:0,getResponseHeader:function(key){var match;if(state===2){if(!responseHeaders){responseHeaders={};while(match=rheaders.exec(responseHeadersString)){responseHeaders[match[1].toLowerCase()]=match[2]}}match=responseHeaders[key.toLowerCase()]}return match==null?null:match},getAllResponseHeaders:function(){return state===2?responseHeadersString:null},setRequestHeader:function(name,value){var lname=name.toLowerCase();if(!state){name=requestHeadersNames[lname]=requestHeadersNames[lname]||name;requestHeaders[name]=value}return this},overrideMimeType:function(type){if(!state){s.mimeType=type}return this},statusCode:function(map){var code;if(map){if(state<2){for(code in map){statusCode[code]=[statusCode[code],map[code]]}}else{jqXHR.always(map[jqXHR.status])}}return this},abort:function(statusText){var finalText=statusText||strAbort;if(transport){transport.abort(finalText)}done(0,finalText);return this}};deferred.promise(jqXHR).complete=completeDeferred.add;jqXHR.success=jqXHR.done;jqXHR.error=jqXHR.fail;s.url=((url||s.url||ajaxLocation)+"").replace(rhash,"").replace(rprotocol,ajaxLocParts[1]+"//");s.type=options.method||options.type||s.method||s.type;s.dataTypes=jQuery.trim(s.dataType||"*").toLowerCase().match(rnotwhite)||[""];if(s.crossDomain==null){parts=rurl.exec(s.url.toLowerCase());s.crossDomain=!!(parts&&(parts[1]!==ajaxLocParts[1]||parts[2]!==ajaxLocParts[2]||(parts[3]||(parts[1]==="http:"?"80":"443"))!==(ajaxLocParts[3]||(ajaxLocParts[1]==="http:"?"80":"443"))))}if(s.data&&s.processData&&typeof s.data!=="string"){s.data=jQuery.param(s.data,s.traditional)}inspectPrefiltersOrTransports(prefilters,s,options,jqXHR);if(state===2){return jqXHR}fireGlobals=jQuery.event&&s.global;if(fireGlobals&&jQuery.active++===0){jQuery.event.trigger("ajaxStart")}s.type=s.type.toUpperCase();s.hasContent=!rnoContent.test(s.type);cacheURL=s.url;if(!s.hasContent){if(s.data){cacheURL=s.url+=(rquery.test(cacheURL)?"&":"?")+s.data;delete s.data}if(s.cache===false){s.url=rts.test(cacheURL)?cacheURL.replace(rts,"$1_="+nonce++):cacheURL+(rquery.test(cacheURL)?"&":"?")+"_="+nonce++}}if(s.ifModified){if(jQuery.lastModified[cacheURL]){jqXHR.setRequestHeader("If-Modified-Since",jQuery.lastModified[cacheURL])}if(jQuery.etag[cacheURL]){jqXHR.setRequestHeader("If-None-Match",jQuery.etag[cacheURL])}}if(s.data&&s.hasContent&&s.contentType!==false||options.contentType){jqXHR.setRequestHeader("Content-Type",s.contentType)}jqXHR.setRequestHeader("Accept",s.dataTypes[0]&&s.accepts[s.dataTypes[0]]?s.accepts[s.dataTypes[0]]+(s.dataTypes[0]!=="*"?", "+allTypes+"; q=0.01":""):s.accepts["*"]);for(i in s.headers){jqXHR.setRequestHeader(i,s.headers[i])}if(s.beforeSend&&(s.beforeSend.call(callbackContext,jqXHR,s)===false||state===2)){return jqXHR.abort()}strAbort="abort";for(i in{success:1,error:1,complete:1}){jqXHR[i](s[i])}transport=inspectPrefiltersOrTransports(transports,s,options,jqXHR);if(!transport){done(-1,"No Transport")}else{jqXHR.readyState=1;if(fireGlobals){globalEventContext.trigger("ajaxSend",[jqXHR,s])}if(s.async&&s.timeout>0){timeoutTimer=setTimeout(function(){jqXHR.abort("timeout")},s.timeout)}try{state=1;transport.send(requestHeaders,done)}catch(e){if(state<2){done(-1,e)}else{throw e}}}function done(status,nativeStatusText,responses,headers){var isSuccess,success,error,response,modified,statusText=nativeStatusText;if(state===2){return}state=2;if(timeoutTimer){clearTimeout(timeoutTimer)}transport=undefined;responseHeadersString=headers||"";jqXHR.readyState=status>0?4:0;isSuccess=status>=200&&status<300||status===304;if(responses){response=ajaxHandleResponses(s,jqXHR,responses)}response=ajaxConvert(s,response,jqXHR,isSuccess);if(isSuccess){if(s.ifModified){modified=jqXHR.getResponseHeader("Last-Modified");if(modified){jQuery.lastModified[cacheURL]=modified}modified=jqXHR.getResponseHeader("etag");if(modified){jQuery.etag[cacheURL]=modified}}if(status===204||s.type==="HEAD"){statusText="nocontent"}else if(status===304){statusText="notmodified"}else{statusText=response.state;success=response.data;error=response.error;isSuccess=!error}}else{error=statusText;if(status||!statusText){statusText="error";if(status<0){status=0}}}jqXHR.status=status;jqXHR.statusText=(nativeStatusText||statusText)+"";if(isSuccess){deferred.resolveWith(callbackContext,[success,statusText,jqXHR])}else{deferred.rejectWith(callbackContext,[jqXHR,statusText,error])}jqXHR.statusCode(statusCode);statusCode=undefined;if(fireGlobals){globalEventContext.trigger(isSuccess?"ajaxSuccess":"ajaxError",[jqXHR,s,isSuccess?success:error])}completeDeferred.fireWith(callbackContext,[jqXHR,statusText]);if(fireGlobals){globalEventContext.trigger("ajaxComplete",[jqXHR,s]);if(!--jQuery.active){jQuery.event.trigger("ajaxStop")}}}return jqXHR},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json")},getScript:function(url,callback){return jQuery.get(url,undefined,callback,"script")}});jQuery.each(["get","post"],function(i,method){jQuery[method]=function(url,data,callback,type){if(jQuery.isFunction(data)){type=type||callback;callback=data;data=undefined}return jQuery.ajax({url:url,type:method,dataType:type,data:data,success:callback})}});jQuery._evalUrl=function(url){return jQuery.ajax({url:url,type:"GET",dataType:"script",async:false,global:false,"throws":true})};jQuery.fn.extend({wrapAll:function(html){if(jQuery.isFunction(html)){return this.each(function(i){jQuery(this).wrapAll(html.call(this,i))})}if(this[0]){var wrap=jQuery(html,this[0].ownerDocument).eq(0).clone(true);if(this[0].parentNode){wrap.insertBefore(this[0])}wrap.map(function(){var elem=this;while(elem.firstChild&&elem.firstChild.nodeType===1){elem=elem.firstChild}return elem}).append(this)}return this},wrapInner:function(html){if(jQuery.isFunction(html)){return this.each(function(i){jQuery(this).wrapInner(html.call(this,i))})}return this.each(function(){var self=jQuery(this),contents=self.contents();if(contents.length){contents.wrapAll(html)}else{self.append(html)}})},wrap:function(html){var isFunction=jQuery.isFunction(html);return this.each(function(i){jQuery(this).wrapAll(isFunction?html.call(this,i):html)})},unwrap:function(){return this.parent().each(function(){if(!jQuery.nodeName(this,"body")){jQuery(this).replaceWith(this.childNodes)}}).end()}});jQuery.expr.filters.hidden=function(elem){return elem.offsetWidth<=0&&elem.offsetHeight<=0||!support.reliableHiddenOffsets()&&(elem.style&&elem.style.display||jQuery.css(elem,"display"))==="none"};jQuery.expr.filters.visible=function(elem){return!jQuery.expr.filters.hidden(elem)};var r20=/%20/g,rbracket=/\[\]$/,rCRLF=/\r?\n/g,rsubmitterTypes=/^(?:submit|button|image|reset|file)$/i,rsubmittable=/^(?:input|select|textarea|keygen)/i;function buildParams(prefix,obj,traditional,add){var name;if(jQuery.isArray(obj)){jQuery.each(obj,function(i,v){if(traditional||rbracket.test(prefix)){add(prefix,v)}else{buildParams(prefix+"["+(typeof v==="object"?i:"")+"]",v,traditional,add)}})}else if(!traditional&&jQuery.type(obj)==="object"){for(name in obj){buildParams(prefix+"["+name+"]",obj[name],traditional,add)}}else{add(prefix,obj)}}jQuery.param=function(a,traditional){var prefix,s=[],add=function(key,value){value=jQuery.isFunction(value)?value():value==null?"":value;s[s.length]=encodeURIComponent(key)+"="+encodeURIComponent(value)};if(traditional===undefined){traditional=jQuery.ajaxSettings&&jQuery.ajaxSettings.traditional}if(jQuery.isArray(a)||a.jquery&&!jQuery.isPlainObject(a)){jQuery.each(a,function(){add(this.name,this.value)})}else{for(prefix in a){buildParams(prefix,a[prefix],traditional,add)}}return s.join("&").replace(r20,"+")};jQuery.fn.extend({serialize:function(){return jQuery.param(this.serializeArray()); -},serializeArray:function(){return this.map(function(){var elements=jQuery.prop(this,"elements");return elements?jQuery.makeArray(elements):this}).filter(function(){var type=this.type;return this.name&&!jQuery(this).is(":disabled")&&rsubmittable.test(this.nodeName)&&!rsubmitterTypes.test(type)&&(this.checked||!rcheckableType.test(type))}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:jQuery.isArray(val)?jQuery.map(val,function(val){return{name:elem.name,value:val.replace(rCRLF,"\r\n")}}):{name:elem.name,value:val.replace(rCRLF,"\r\n")}}).get()}});jQuery.ajaxSettings.xhr=window.ActiveXObject!==undefined?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&createStandardXHR()||createActiveXHR()}:createStandardXHR;var xhrId=0,xhrCallbacks={},xhrSupported=jQuery.ajaxSettings.xhr();if(window.attachEvent){window.attachEvent("onunload",function(){for(var key in xhrCallbacks){xhrCallbacks[key](undefined,true)}})}support.cors=!!xhrSupported&&"withCredentials"in xhrSupported;xhrSupported=support.ajax=!!xhrSupported;if(xhrSupported){jQuery.ajaxTransport(function(options){if(!options.crossDomain||support.cors){var callback;return{send:function(headers,complete){var i,xhr=options.xhr(),id=++xhrId;xhr.open(options.type,options.url,options.async,options.username,options.password);if(options.xhrFields){for(i in options.xhrFields){xhr[i]=options.xhrFields[i]}}if(options.mimeType&&xhr.overrideMimeType){xhr.overrideMimeType(options.mimeType)}if(!options.crossDomain&&!headers["X-Requested-With"]){headers["X-Requested-With"]="XMLHttpRequest"}for(i in headers){if(headers[i]!==undefined){xhr.setRequestHeader(i,headers[i]+"")}}xhr.send(options.hasContent&&options.data||null);callback=function(_,isAbort){var status,statusText,responses;if(callback&&(isAbort||xhr.readyState===4)){delete xhrCallbacks[id];callback=undefined;xhr.onreadystatechange=jQuery.noop;if(isAbort){if(xhr.readyState!==4){xhr.abort()}}else{responses={};status=xhr.status;if(typeof xhr.responseText==="string"){responses.text=xhr.responseText}try{statusText=xhr.statusText}catch(e){statusText=""}if(!status&&options.isLocal&&!options.crossDomain){status=responses.text?200:404}else if(status===1223){status=204}}}if(responses){complete(status,statusText,responses,xhr.getAllResponseHeaders())}};if(!options.async){callback()}else if(xhr.readyState===4){setTimeout(callback)}else{xhr.onreadystatechange=xhrCallbacks[id]=callback}},abort:function(){if(callback){callback(undefined,true)}}}}})}function createStandardXHR(){try{return new window.XMLHttpRequest}catch(e){}}function createActiveXHR(){try{return new window.ActiveXObject("Microsoft.XMLHTTP")}catch(e){}}jQuery.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(text){jQuery.globalEval(text);return text}}});jQuery.ajaxPrefilter("script",function(s){if(s.cache===undefined){s.cache=false}if(s.crossDomain){s.type="GET";s.global=false}});jQuery.ajaxTransport("script",function(s){if(s.crossDomain){var script,head=document.head||jQuery("head")[0]||document.documentElement;return{send:function(_,callback){script=document.createElement("script");script.async=true;if(s.scriptCharset){script.charset=s.scriptCharset}script.src=s.url;script.onload=script.onreadystatechange=function(_,isAbort){if(isAbort||!script.readyState||/loaded|complete/.test(script.readyState)){script.onload=script.onreadystatechange=null;if(script.parentNode){script.parentNode.removeChild(script)}script=null;if(!isAbort){callback(200,"success")}}};head.insertBefore(script,head.firstChild)},abort:function(){if(script){script.onload(undefined,true)}}}}});var oldCallbacks=[],rjsonp=/(=)\?(?=&|$)|\?\?/;jQuery.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var callback=oldCallbacks.pop()||jQuery.expando+"_"+nonce++;this[callback]=true;return callback}});jQuery.ajaxPrefilter("json jsonp",function(s,originalSettings,jqXHR){var callbackName,overwritten,responseContainer,jsonProp=s.jsonp!==false&&(rjsonp.test(s.url)?"url":typeof s.data==="string"&&!(s.contentType||"").indexOf("application/x-www-form-urlencoded")&&rjsonp.test(s.data)&&"data");if(jsonProp||s.dataTypes[0]==="jsonp"){callbackName=s.jsonpCallback=jQuery.isFunction(s.jsonpCallback)?s.jsonpCallback():s.jsonpCallback;if(jsonProp){s[jsonProp]=s[jsonProp].replace(rjsonp,"$1"+callbackName)}else if(s.jsonp!==false){s.url+=(rquery.test(s.url)?"&":"?")+s.jsonp+"="+callbackName}s.converters["script json"]=function(){if(!responseContainer){jQuery.error(callbackName+" was not called")}return responseContainer[0]};s.dataTypes[0]="json";overwritten=window[callbackName];window[callbackName]=function(){responseContainer=arguments};jqXHR.always(function(){window[callbackName]=overwritten;if(s[callbackName]){s.jsonpCallback=originalSettings.jsonpCallback;oldCallbacks.push(callbackName)}if(responseContainer&&jQuery.isFunction(overwritten)){overwritten(responseContainer[0])}responseContainer=overwritten=undefined});return"script"}});jQuery.parseHTML=function(data,context,keepScripts){if(!data||typeof data!=="string"){return null}if(typeof context==="boolean"){keepScripts=context;context=false}context=context||document;var parsed=rsingleTag.exec(data),scripts=!keepScripts&&[];if(parsed){return[context.createElement(parsed[1])]}parsed=jQuery.buildFragment([data],context,scripts);if(scripts&&scripts.length){jQuery(scripts).remove()}return jQuery.merge([],parsed.childNodes)};var _load=jQuery.fn.load;jQuery.fn.load=function(url,params,callback){if(typeof url!=="string"&&_load){return _load.apply(this,arguments)}var selector,response,type,self=this,off=url.indexOf(" ");if(off>=0){selector=jQuery.trim(url.slice(off,url.length));url=url.slice(0,off)}if(jQuery.isFunction(params)){callback=params;params=undefined}else if(params&&typeof params==="object"){type="POST"}if(self.length>0){jQuery.ajax({url:url,type:type,dataType:"html",data:params}).done(function(responseText){response=arguments;self.html(selector?jQuery("
").append(jQuery.parseHTML(responseText)).find(selector):responseText)}).complete(callback&&function(jqXHR,status){self.each(callback,response||[jqXHR.responseText,status,jqXHR])})}return this};jQuery.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(i,type){jQuery.fn[type]=function(fn){return this.on(type,fn)}});jQuery.expr.filters.animated=function(elem){return jQuery.grep(jQuery.timers,function(fn){return elem===fn.elem}).length};var docElem=window.document.documentElement;function getWindow(elem){return jQuery.isWindow(elem)?elem:elem.nodeType===9?elem.defaultView||elem.parentWindow:false}jQuery.offset={setOffset:function(elem,options,i){var curPosition,curLeft,curCSSTop,curTop,curOffset,curCSSLeft,calculatePosition,position=jQuery.css(elem,"position"),curElem=jQuery(elem),props={};if(position==="static"){elem.style.position="relative"}curOffset=curElem.offset();curCSSTop=jQuery.css(elem,"top");curCSSLeft=jQuery.css(elem,"left");calculatePosition=(position==="absolute"||position==="fixed")&&jQuery.inArray("auto",[curCSSTop,curCSSLeft])>-1;if(calculatePosition){curPosition=curElem.position();curTop=curPosition.top;curLeft=curPosition.left}else{curTop=parseFloat(curCSSTop)||0;curLeft=parseFloat(curCSSLeft)||0}if(jQuery.isFunction(options)){options=options.call(elem,i,curOffset)}if(options.top!=null){props.top=options.top-curOffset.top+curTop}if(options.left!=null){props.left=options.left-curOffset.left+curLeft}if("using"in options){options.using.call(elem,props)}else{curElem.css(props)}}};jQuery.fn.extend({offset:function(options){if(arguments.length){return options===undefined?this:this.each(function(i){jQuery.offset.setOffset(this,options,i)})}var docElem,win,box={top:0,left:0},elem=this[0],doc=elem&&elem.ownerDocument;if(!doc){return}docElem=doc.documentElement;if(!jQuery.contains(docElem,elem)){return box}if(typeof elem.getBoundingClientRect!==strundefined){box=elem.getBoundingClientRect()}win=getWindow(doc);return{top:box.top+(win.pageYOffset||docElem.scrollTop)-(docElem.clientTop||0),left:box.left+(win.pageXOffset||docElem.scrollLeft)-(docElem.clientLeft||0)}},position:function(){if(!this[0]){return}var offsetParent,offset,parentOffset={top:0,left:0},elem=this[0];if(jQuery.css(elem,"position")==="fixed"){offset=elem.getBoundingClientRect()}else{offsetParent=this.offsetParent();offset=this.offset();if(!jQuery.nodeName(offsetParent[0],"html")){parentOffset=offsetParent.offset()}parentOffset.top+=jQuery.css(offsetParent[0],"borderTopWidth",true);parentOffset.left+=jQuery.css(offsetParent[0],"borderLeftWidth",true)}return{top:offset.top-parentOffset.top-jQuery.css(elem,"marginTop",true),left:offset.left-parentOffset.left-jQuery.css(elem,"marginLeft",true)}},offsetParent:function(){return this.map(function(){var offsetParent=this.offsetParent||docElem;while(offsetParent&&(!jQuery.nodeName(offsetParent,"html")&&jQuery.css(offsetParent,"position")==="static")){offsetParent=offsetParent.offsetParent}return offsetParent||docElem})}});jQuery.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(method,prop){var top=/Y/.test(prop);jQuery.fn[method]=function(val){return access(this,function(elem,method,val){var win=getWindow(elem);if(val===undefined){return win?prop in win?win[prop]:win.document.documentElement[method]:elem[method]}if(win){win.scrollTo(!top?val:jQuery(win).scrollLeft(),top?val:jQuery(win).scrollTop())}else{elem[method]=val}},method,val,arguments.length,null)}});jQuery.each(["top","left"],function(i,prop){jQuery.cssHooks[prop]=addGetHookIf(support.pixelPosition,function(elem,computed){if(computed){computed=curCSS(elem,prop);return rnumnonpx.test(computed)?jQuery(elem).position()[prop]+"px":computed}})});jQuery.each({Height:"height",Width:"width"},function(name,type){jQuery.each({padding:"inner"+name,content:type,"":"outer"+name},function(defaultExtra,funcName){jQuery.fn[funcName]=function(margin,value){var chainable=arguments.length&&(defaultExtra||typeof margin!=="boolean"),extra=defaultExtra||(margin===true||value===true?"margin":"border");return access(this,function(elem,type,value){var doc;if(jQuery.isWindow(elem)){return elem.document.documentElement["client"+name]}if(elem.nodeType===9){doc=elem.documentElement;return Math.max(elem.body["scroll"+name],doc["scroll"+name],elem.body["offset"+name],doc["offset"+name],doc["client"+name])}return value===undefined?jQuery.css(elem,type,extra):jQuery.style(elem,type,value,extra)},type,chainable?margin:undefined,chainable,null)}})});jQuery.fn.size=function(){return this.length};jQuery.fn.andSelf=jQuery.fn.addBack;if(typeof define==="function"&&define.amd){define("jquery",[],function(){return jQuery})}var _jQuery=window.jQuery,_$=window.$;jQuery.noConflict=function(deep){if(window.$===jQuery){window.$=_$}if(deep&&window.jQuery===jQuery){window.jQuery=_jQuery}return jQuery};if(typeof noGlobal===strundefined){window.jQuery=window.$=jQuery}return jQuery})},{}],13:[function(require,module,exports){"use strict";var extend=require("backbone-extend-standalone");var Promise=require("es6-promise").Promise;var authz=require("./authz");var identity=require("./identity");var notification=require("./notification");var registry=require("./registry");var storage=require("./storage");function App(){this.modules=[];this.registry=new registry.Registry;this._started=false;this.registry.registerUtility(notification.defaultNotifier,"notifier");this.include(authz.acl);this.include(identity.simple);this.include(storage.noop)}App.prototype.include=function(module,options){var mod=module(options);if(typeof mod.configure==="function"){mod.configure(this.registry)}this.modules.push(mod);return this};App.prototype.start=function(){if(this._started){return}this._started=true;var self=this;var reg=this.registry;this.authz=reg.getUtility("authorizationPolicy");this.ident=reg.getUtility("identityPolicy");this.notify=reg.getUtility("notifier");this.annotations=new storage.StorageAdapter(reg.getUtility("storage"),function(){return self.runHook.apply(self,arguments)});return this.runHook("start",[this])};App.prototype.destroy=function(){return this.runHook("destroy")};App.prototype.runHook=function(name,args){var results=[];for(var i=0,len=this.modules.length;i1){return}event.preventDefault();this.ignoreMouseup=true},_onMouseup:function(event){if(event.which>1){return}if(this.ignoreMouseup){event.stopImmediatePropagation()}},_onClick:function(event){if(event.which>1){return}event.preventDefault();this.hide();this.ignoreMouseup=false;if(this.annotation!==null&&typeof this.onCreate==="function"){this.onCreate(this.annotation,event)}}});Adder.template=['
',' ","
"].join("\n");Adder.options={onCreate:null};exports.Adder=Adder},{"../util":30,"./widget":29}],21:[function(require,module,exports){"use strict";var Widget=require("./widget").Widget,util=require("../util");var $=util.$;var _t=util.gettext;var Promise=util.Promise;var NS="annotator-editor";var id=function(){var counter;counter=-1;return function(){return counter+=1}}();function preventEventDefault(event){if(typeof event!=="undefined"&&event!==null&&typeof event.preventDefault==="function"){event.preventDefault()}}var dragTracker=exports.dragTracker=function dragTracker(handle,callback){var lastPos=null,throttled=false;function mouseMove(e){if(throttled||lastPos===null){return}var delta={y:e.pageY-lastPos.top,x:e.pageX-lastPos.left};var trackLastMove=true;if(typeof callback==="function"){trackLastMove=callback(delta)}if(trackLastMove!==false){lastPos={top:e.pageY,left:e.pageX}}throttled=true;setTimeout(function(){throttled=false},1e3/60)}function mouseUp(){lastPos=null;$(handle.ownerDocument).off("mouseup",mouseUp).off("mousemove",mouseMove)}function mouseDown(e){if(e.target!==handle){return}lastPos={top:e.pageY,left:e.pageX};$(handle.ownerDocument).on("mouseup",mouseUp).on("mousemove",mouseMove);e.preventDefault()}function destroy(){$(handle).off("mousedown",mouseDown)}$(handle).on("mousedown",mouseDown);return{destroy:destroy}};var resizer=exports.resizer=function resizer(element,handle,options){var $el=$(element);if(typeof options==="undefined"||options===null){options={}}function translate(delta){var directionX=1,directionY=-1;if(typeof options.invertedX==="function"&&options.invertedX()){directionX=-1}if(typeof options.invertedY==="function"&&options.invertedY()){directionY=1}return{x:delta.x*directionX,y:delta.y*directionY}}function resize(delta){var height=$el.height(),width=$el.width(),translated=translate(delta);if(Math.abs(translated.x)>0){$el.width(width+translated.x)}if(Math.abs(translated.y)>0){$el.height(height+translated.y)}var didChange=$el.height()!==height||$el.width()!==width;return didChange}return dragTracker(handle,resize)};var mover=exports.mover=function mover(element,handle){function move(delta){$(element).css({top:parseInt($(element).css("top"),10)+delta.y,left:parseInt($(element).css("left"),10)+delta.x})}return dragTracker(handle,move)};var Editor=exports.Editor=Widget.extend({constructor:function(options){Widget.call(this,options);this.fields=[];this.annotation={};if(this.options.defaultFields){this.addField({type:"textarea",label:_t("Comments")+"…",load:function(field,annotation){$(field).find("textarea").val(annotation.text||"")},submit:function(field,annotation){annotation.text=$(field).find("textarea").val()}})}var self=this;this.element.on("submit."+NS,"form",function(e){self._onFormSubmit(e)}).on("click."+NS,".annotator-save",function(e){self._onSaveClick(e)}).on("click."+NS,".annotator-cancel",function(e){self._onCancelClick(e)}).on("mouseover."+NS,".annotator-cancel",function(e){self._onCancelMouseover(e)}).on("keydown."+NS,"textarea",function(e){self._onTextareaKeydown(e)})},destroy:function(){this.element.off("."+NS);Widget.prototype.destroy.call(this)},show:function(position){if(typeof position!=="undefined"&&position!==null){this.element.css({top:position.top,left:position.left})}this.element.find(".annotator-save").addClass(this.classes.focus);Widget.prototype.show.call(this);this.element.find(":input:first").focus();this._setupDraggables()},load:function(annotation,position){this.annotation=annotation;for(var i=0,len=this.fields.length;i');field.element=element[0];if(field.type==="textarea"){input=$("",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,ba=/<([\w:]+)/,ca=/<|&#?\w+;/,da=/<(?:script|style|link)/i,ea=/checked\s*(?:[^=]|=\s*.checked.)/i,fa=/^$|\/(?:java|ecma)script/i,ga=/^true\/(.*)/,ha=/^\s*\s*$/g,ia={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ia.optgroup=ia.option,ia.tbody=ia.tfoot=ia.colgroup=ia.caption=ia.thead,ia.th=ia.td;function ja(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function ka(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function la(a){var b=ga.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function ma(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function na(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function oa(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pa(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=oa(h),f=oa(a),d=0,e=f.length;e>d;d++)pa(f[d],g[d]);if(b)if(c)for(f=f||oa(a),g=g||oa(h),d=0,e=f.length;e>d;d++)na(f[d],g[d]);else na(a,h);return g=oa(h,"script"),g.length>0&&ma(g,!i&&oa(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(ca.test(e)){f=f||k.appendChild(b.createElement("div")),g=(ba.exec(e)||["",""])[1].toLowerCase(),h=ia[g]||ia._default,f.innerHTML=h[1]+e.replace(aa,"<$1>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=oa(k.appendChild(e),"script"),i&&ma(f),c)){j=0;while(e=f[j++])fa.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(oa(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&ma(oa(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(oa(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!da.test(a)&&!ia[(ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(aa,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(oa(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(oa(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&ea.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(oa(c,"script"),ka),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,oa(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,la),j=0;g>j;j++)h=f[j],fa.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(ha,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qa,ra={};function sa(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function ta(a){var b=l,c=ra[a];return c||(c=sa(a,b),"none"!==c&&c||(qa=(qa||n("